Programming declaration constraints
In C #, a program can provide a list of optional constraints for each type parameter declared in a generic class. Constraints represent the requirements that must be met to construct a type as a generic. You can use the WHERE keyword to declare a constraint, followed by a parameter-requirement pair, where "parameter" must be a parameter defined in the generic, and "requirement" must be a class or interface.
To satisfy the need to use the CompareTo method in the Dictionary class, the program can add constraints to the KeyType type parameter, requiring that any type passed to the Dictionary class as the first parameter must implement the IComparable interface, for example:
public class Dictionary<keytype, valtype> where keytype:icomparable
{
public void Add (KeyType key, Valtype val)
{
...
Switch (Key.compareto (x))
{
}
...
}
}
This way, the code is checked when the code is compiled to ensure that the type passed as the first parameter implements the IComparable interface each time the program uses the Dictionary class. In addition, the program does not have to explicitly convert the variable to the IComparable interface until the CompareTo method is invoked.
Multiple constraints
For any given type parameter, the program can specify any number of interface constraints for it, but only one class constraint at a maximum. Each new constraint is declared in the form of another "parameter-requirement" pair, and each constraint for a given generic is separated by commas. The Dictionary class in the following example contains two parameters, KeyType and Valtype. The KeyType type parameter has two interface constraints, and the Valtype type parameter has a class constraint:
public class Dictionary<keytype, valtype> where
Keytype:icomparable,
Keytype:ienumerable,
Valtype:customer
{
public void Add (KeyType key, Valtype val)
{
...
Switch (Key.compareto (x))
{
}
...
}
}
Generics at run time
There is almost no difference between the compilation method of a generic class and the normal class. In fact, the result of compilation is simply metadata and intermediate language (IL). Of course, the IL should be parameterized in order to accept the type provided by the user in the code. The usage of the IL of a generic is different depending on whether the supplied type parameter is a value type or a reference type.
When you first construct a generic with a value type as a parameter, the runtime replaces the corresponding location in the IL with the supplied arguments to create a specialized generic. For each unique value type used as a parameter, a dedicated generic is created at once.
For example, suppose the program code declares a Stack constructed of integers:
Stack<int> Stack;
At this point, the runtime generates a dedicated Stack class and replaces the corresponding arguments for the class with an integer. Now, regardless of when the program code uses an integer stack, the runtime will reuse the generated private stack class. The following example creates two instances of the integer stack, each of which is created using the code generated by the runtime of this integer stack:
stack<int> Stackone = new stack<int> ();
stack<int> stacktwo = new stack<int> ();
However, if you create a Stack class elsewhere in your program code, and uses different value types (such as long or user-defined structures) as their arguments, the runtime generates other forms of generics, which then replaces the long integer parameters in the corresponding IL position. The advantage of creating a specialized class for generics constructed with value types is that better performance can be achieved. After all, each dedicated generic class contains a value type in "local", so no more conversions are necessary.
Generics work in a slightly different way from reference types. When you first construct a generic with any reference type, the runtime replaces the arguments in the IL with an object reference to create a specialized generic. After that, whenever a constructed type is instantiated using a reference type as a parameter, regardless of the type being constructed, the runtime repeats the private generics created earlier.
For example, suppose you have two reference types, the customer class, and the Order class, and further assume that you created the customer type Stack:
Stack<customer> customers;
At this point, the runtime generates a private Stack class that does not store data, but instead stores the object references that are subsequently populated. Suppose the next line of code creates a Stack of other reference types, called Order:
stack<order> orders = new stack<order> ();
Unlike a value type, there is no other specialized stack class created for the order type, but instead creates an instance of the private stack class and sets the orders variable to reference it. For each object reference that replaces the type parameter, allocates the memory space according to the size of the order type and sets the pointer to reference the memory location. Suppose you subsequently encountered a line of code to create a Stack of the Customer type:
Customers = new stack<customer> ();
As with the previous stack class created with the order type, another instance of the private Stack class is created and the pointers contained in it are set to memory regions that reference the size of the Customer type. Because different programs have significant differences in the number of reference types, the C # Implementation of generics greatly reduces the rate of code expansion by reducing the number of reference types to the number of specialized classes created by the compiler for generic classes of reference types.
In addition, when you instantiate a generic C # class with a type parameter, whether it is a value type or a reference type, you can query with reflection and actual types at run time, and you can determine its type parameters.
Differences between C # generics and other implementations
There is a significant difference between C + + templates and C # generics. C # generics are compiled into IL, which makes it intelligently to create the appropriate specialized type for each value type at run time, but only once for a reference type; C + + templates are actually code extension macros that generate a specialized type for each type parameter provided to the template. Therefore, when the C + + compiler encounters a template (such as an integer stack), it expands the template code into a stack class and includes the integer as the type of the class itself. Whether a type parameter is a value type or a reference type, if you do not specifically design a linker to reduce code bloat, the C + + compiler creates a specialized class each time, resulting in a more significant code expansion than using C # generics.
Also, C + + templates cannot define constraints. C + + templates implicitly define constraints only by using one member, which may or may not be part of the type parameter. If the member is eventually passed to the type parameter of the generic class, the program will run correctly. Otherwise, the program will fail and may return a hidden error message. Because C # generics can declare constraints and have strict types, these potential errors do not exist.
Now, Sun microsystems® has added additional generics in the new version of the Java language (code name "Tiger"). Sun-selected implementations do not need to modify the Java virtual machine. Therefore, Sun is faced with the problem of how to implement generics on unmodified virtual machines.
The proposed Java implementation uses similar syntax, including type parameters and constraints, with templates in C + + and generics in C #. However, because it handles value types differently from handling reference types, the unmodified Java virtual machine does not support generics of value types. As a result, generics in Java cannot be executed effectively. In fact, the Java compiler inserts an automatic downward type conversion from the specified constraint (if declared) or the Base object type (if no constraints are declared) when the data needs to be returned. In addition, the Java compiler will generate a private type at run time and then use it to instantiate any constructed type. Finally, because the Java virtual machine itself does not support generics, it is not possible to determine the type parameters of a generic instance at run time, and other uses of reflection are severely restricted.
Generics support in other languages
Microsoft's goal is to support the use and creation of generics in Visual J # (TM), Visual C + +, and visual Basic. Although the time to implement this feature is early in different languages, all the other three languages of Microsoft will include support for generics. At the same time, the C # team is working on adding the appropriate functionality to the basic runtime of generics, laying the groundwork for multi-language support. Microsoft works closely with third party language partners to ensure that generics are created and used in a. NET based language.
Iterative Program
An iterator is a language that is built on the study of similar functions in languages, such as CLU, Sather, and Icon. Simply put, a type can easily declare the way a foreach statement iterates its elements through an iterator.
Why do I need an iterator
Now, if the class needs to support iterative operations using the Foreach loop structure, they must implement the enumerator pattern. For example, the compiler extends the Foreach loop structure on the left to the while loop structure on the right:
List List = ...;
foreach (object obj in list)
{
DoSomething (obj);
}
Enumerator e = list. GetEnumerator ();
while (E.movenext ())
{
Object obj = e.current;
DoSomething (obj);
It is noteworthy that in order for the Foreach loop to work correctly, the List data structure (an instance of the iteration) must support the GetEnumerator function. After you create the List data structure, you must implement the GetEnumerator function to return the Listenumerator object:
public class List
{
Internal object[] elements;
internal int count;
Public Listenumerator GetEnumerator ()
{
Return to New Listenumerator (this);
}
}
The Listenumerator object you create must not only implement the current property and the MoveNext method, but also maintain its internal state so that the program can move to the next item each time the loop is executed. This internal state machine is simpler for the List data structure, but for data structures (such as binary trees) that require a recursive loop, the state machine will be quite complex.
Because implementing this enumerator pattern requires the developer to devote a lot of effort and write a lot of code, C # contains a new structure that makes it easy for a class to indicate how the Foreach loop iterates over its content.
Defining an iterator
Because an iterator is a logical counterpart to a Foreach loop structure, it is defined like a function: Use the foreach keyword followed by a pair of parentheses. In the following example, the program declares an iterator for the List type. The return type of the iterator is determined by the user, but because the List class stores the object type internally, the return type of the following iterator sample is an object:
public class List
{
Internal object[] elements;
internal int count;
public Object foreach ()
{
}
}
It is noteworthy that after implementing the enumerator pattern, the program needs to maintain an internal state machine to track the location of the program in the data structure. An iterator has a built-in state machine. With the new yield keyword, a program can return a value to a foreach statement that invokes the iterator. When the foreach statement next loops and calls the iterator again, the iterator begins execution at the point where the last yield statement stopped. In the following example, the program generates three string types:
public class List
{
Internal object[] elements;
internal int count;
public string foreach ()
{
Yield "Microsoft";
Yield "Corporation";
Yield "Developer Division";
}
}
In the following example, the Foreach loop that calls this iterator executes three times, each time receiving a string in the order specified by the first three yield statements:
List List = new List ();
foreach (string s in list)
{
Console.WriteLine (s);
}
If you want the program to implement an iterator to traverse the elements in the list, you need to use a Foreach loop to modify this iterator to traverse the array of elements and produce each of the items in the array in each iteration:
public class List
{
Internal object[] elements;
internal int count;
public Object foreach ()
{
foreach (object o in elements)
{
Yield o;
}
}
}
How the iterative process works
The iterator represents the program that handles the day-to-day operation of implementing the enumerator pattern. The C # compiler converts the code that you write in an iterator into the corresponding class and code that uses the enumerator pattern without having to create a class and build a state machine. In this way, the iterative program significantly improves the productivity of the developer.
anonymous method
An anonymous method is another practical language structure that enables programmers to create blocks of code that can be boxed in a delegate and executed later. They are based on language concepts called lambda functions and are similar to the corresponding language concepts in Lisp and Python.
Create delegate Code
A delegate is an object that refers to a method. When a delegate is invoked, the method it refers to is invoked. The following example illustrates a simple form that contains a list box, text box, and button three controls. When the button is initialized, the program instructs its Click delegate to refer to the AddClick method that is stored elsewhere in the object. In the AddClick method, the value of the text box is stored in the list box. Because the AddClick method is added to the click Delegate of the button instance, this method is called each time the button is clicked.
public class MyForm
{
ListBox listbox;
TextBox textbox;
Button button;
Public MyForm ()
{
ListBox = new ListBox (...);
TextBox = new TextBox (...);
button = New button (...);
button. Click + + new EventHandler (AddClick);
}
void AddClick (object sender, EventArgs e)
{
LISTBOX.ITEMS.ADD (TextBox.Text);
}
}
Using anonymous Methods
The previous example is very intuitive. A separate function is created, and a delegate reference is made to it, which is called by the program whenever this delegate is invoked. In this function, a series of executable steps are performed. With an anonymous method, a program does not need to create an entire new method for the class, but can directly reference the executable steps contained in the delegate. The method of declaring an anonymous method is to instantiate a delegate, then add a pair of curly braces that represent the execution scope after instantiating the statement, plus a semicolon to terminate the statement.
In the following example, the program modifies the delegate creation statement to modify the list box directly, rather than referencing the function that represents the program to modify the list box. The purpose of storing the code is to modify the list box in the execution scope after the delegate creation statement.
public class MyForm
{
ListBox listbox;
TextBox textbox;
Button button;
Public MyForm ()
{
ListBox = new ListBox (...);
TextBox = new TextBox (...);
button = New button (...);
button. Click + + new EventHandler (sender, E)
{
LISTBOX.ITEMS.ADD (TextBox.Text);
};
}
}
Note that the code in the anonymous method is how to access and process variables declared outside the scope of its execution. In practice, anonymous methods can refer to variables declared by classes and parameters, or to local variables declared by the method in which they reside.
Passing parameters to anonymous methods
Interestingly, the anonymous method statement contains two parameters, namely sender and E. Looking at the definition of the Click delegate for the Button class, you will find that any function referenced by the delegate must contain two parameters, the first parameter is the object type, and the second parameter is the EventArgs type. In the first example, the program did not use the anonymous method, but instead passed two parameters to the AddClick method, the type being object and EventArgs.
Even if this code is written inline, the delegate must still receive two parameters. In the anonymous method, you must declare the names of two parameters so that the associated code block can use them. When the Click event on the button is triggered, the anonymous method is invoked and the corresponding argument is passed to the method.
How the anonymous method works
When an anonymous delegate is encountered, the C # compiler automatically converts code within its execution scope to a unique named function in a unique named class. The delegate that stores the block of code is then set up to reference the compiler-generated objects and methods. When a delegate is invoked, the "anonymous" method block is executed through the compiler-generated method.
Local type
Although it is a good way for object-oriented programming to maintain all the source code for a type in a single file, sometimes performance constraints make the type larger. In addition, the overhead of splitting a type into a subtype in some cases is unacceptable. Also, programmers often create or use applications to publish source code and modify the result code. Unfortunately, when the source code is released again, all existing source-code modifications will be overwritten.
Local types allow you to split types that contain a large number of source code into multiple different source files for easy development and maintenance. In addition, a local type can be used to separate the part of a computer-generated type from the user-written type, making it easier to supplement or modify the code generated by the tool.
In the following example, classes named Foo are defined in the two C # code files File1.cs and File2.cs. If you do not use a local type, a compilation error occurs because the two classes exist in the same namespace. Using the partial keyword, you can instruct the compiler that other definitions of this class may be included elsewhere.
File1.cs File2.cs
public partial class Foo
{
public void MyFunction ()
{
Perform actions here
}
}
public partial class Foo
{
public void Myotherfunction ()
{
Perform actions here
}
}
At compile time, the C # compiler collects all the definitions of the local type and groups them together. The result IL generated by the compiler shows a single class that is grouped together, rather than being continuously displayed as a separate class for each class.
Conform to the standard
In December 2001, the European Computer Manufacturers Association (ECMA) approved the C # programming language as a standard (ECMA 334). Shortly thereafter, the C # standard was quickly tracked by the International Organization for Standardization (ISO) and was expected to be approved soon. The creation of the C # Standard is an important milestone in the development of the new programming language, and it indicates that many implementations are expected to be written on various operating system platforms in the future. In fact, as we can see from its brief history, many Third-party compiler vendors and researchers have implemented it as a standard and created their own version of the C # compiler.
Microsoft welcomes customers ' feedback on adding the features mentioned above in the C # language, and intends to submit these features to the ongoing language standardization process.
Availability of
The features described below will be implemented in a future version of the C # compiler. In early 2003, the "Everett" version of Visual Studio. NET will contain a version of C # that has been slightly modified to meet the ECMA standard. This version does not contain the features described in this article. Microsoft intends to include these features in the "VS for Yukon" version of Visual Studio, but the specific release date has not been determined.
In the next few months, Microsoft publishes detailed information about these features, including all specifications. Programmers and language design groups are welcome to present their own views and feedback on these features as well as any other language features of interest. You can send e-mail to mailto:sharp@microsoft.com and get in touch with the C # language designer.
More information
C # Community Web site: http://www.csharp.net
Visual C # (tm) Product Web site: http://msdn.microsoft.com/vcsharp
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.