The procedural world has two mysterious elements that are everywhere, but are often imperceptible. They are quiet but harmonious. I give this brother an inappropriate name. One is "protocol" and the other is "constraint ". The dynamic and static languages we often see are essentially two elements: "protocol" and "constraint.
C # is a beautiful and rapidly developing language. It integrates static and dynamic advantages. if used properly, it must be able to combine dynamic and dynamic to present a harmonious beauty. This article is the first part of C # Dynamic and Static Combination Programming. I hope this series can be used to explore how to maximize the potential of dynamic and static combination in C.
I am only a beginner in C #. I still have a superficial understanding of computer theory. I am welcome to criticize and correct mistakes in this article. I would like to add some shortcomings. Thank you!
PS: without special instructions, the "dynamic" or "protocol" mentioned in this Article refer to the runtime protocol of the program, including but not limited to dynamic types; "static" or "constraint" refers to the syntax constraints of the program during compilation, and semantic constraints are classified as protocols.
Ignored protocol
When talking about the "protocol", the first thing that comes to our mind is probably the TCP/IP protocol stack, but in fact we are dealing with the Protocol everywhere. In the following example, do you see that the Protocol has come?
B (){
ArrayList lst = ();
Foreach (string item in lst ){
Console. WriteLine (item. Length );
}
}
Method B Assumes that method A complies with: the returned ArrayList contains all elements of the string type. This is the protocol between them, which is not subject to the static check of the compiler. Therefore, the Protocol implies the uncertainty during running. Method A may include non-string elements in the returned results, which causes B to have an exception during running.
. NET2.0 adds a static type constraint through the generic set:
B (){
List <string> lst = ();
Foreach (string item in lst ){
Console. WriteLine (item );
}
}
In this way, B no longer has to worry about the existence of non-string elements in the lst, thanks to the static type constraints on the wildcard for.
Strong Constraint
Constraints can be divided into strengths and weaknesses. The stronger the constraint, the more secure the static it is. The more it is supported by the compiler. the weaker the constraint, the more dynamic it is, the more flexible it is during running.
Both interfaces and delegation are important methods for separating norms and implementations to achieve abstract programming. They have a common relationship, especially for single-method interfaces and delegation. I often see discussions about the similarities and differences between single-method interfaces and delegation. Many people think they are completely equivalent. In fact, they have obviously different syntax constraints. The interface is a static type constraint, while the delegate is only a static signature constraint. The strength of the two is completely different. In other words, a delegate has more collaborators. As long as it is a method that complies with the signature or an anonymous delegate, it can be called by the delegate, and the object that can be called by the interface must implement this interface.
Let's look at an example: You need to write A class A, which requires the log function internally. A uses the IoC method and does not rely on the specific Logger class. The user injects the specific implementation as needed. At the same time, user B of A wants to use A third-party Logger class.
A. Interface-based IoC Interface ILogger {void Write (string msg );}
Class {
ILogger Logger {get; set ;}
Void F (){}
}
Class B {
G (){
A a = new ();
A. Logger = new LogAdapter (); // inject dependency
A. F ();
}
}
// Pack the Logger of the Third Party
Class LogAdapter: ILogger {
Write (string msg) {// The Logger class of the 3rd party is called here}
}
B. Delegate-based IoC Class {
Action <string> Logging {get; set ;}
Void F (){}
}
Class B {
G (){
A a = new ();
A. Logging = delegate (string msg) {// call the Logger class of the 3rd party };
A. F ();
}
}
Compared with the above two examples, we will find that the delegate is much weaker than the interface constraints, and it is much more flexible to use. The implementation based on the interface has to add an Adapter to mechanically adapt to the interface type constraints, while the implementation based on the delegate only needs to ensure the method signature constraints.
The above discussion about the syntax constraints of interfaces and delegation is not a comprehensive comparison of interfaces and delegation. Here we hope you can understand two different OO polymorphism philosophies: the philosophy of interfaces is what you inherit, what you are, and what you can do what you delegate. These two philosophies reflect the rigorous but inflexible Static Characteristics of the inherited constraints of interfaces in C #, and the flexible but less rigorous Dynamic Characteristics of delegation. This understanding is helpful for understanding Duck Typing.
Follow-up
The subsequent sections will introduce DuckTyping, generic delegation, Expression Tree, lambda, C #4.0 dynamic programming, and other related content. Thank you for your attention!
Modification history:
1. First edition
2. In, the title was changed to "C # dynamic/static Combination Programming ";
Based on the comments from the readers, I would like to add a description of "dynamic" and "static;
Supplemented descriptions of interfaces and Delegation