C # 2.0 specification (i) Introduction

Source: Internet
Author: User

19.c#2.0 Introduction

C#2.0 introduces several language extensions, the most important of which are generics, anonymous methods, iterators, and incomplete types (partial type).

Generics enable classes, structs, interfaces, delegates, and methods to be parameterized by the type of data they store and manipulate. Generics are useful because they provide stronger compile-time type checking, reduced explicit conversions between data types, and boxing and run-time type checking.
Anonymous methods allow code blocks to be infiltrated inline to the desired value of the delegate. Anonymous methods are similar to the λ function (lambda functions) in the Lisp programming language. C#2.0 supports the creation of "closures" in which anonymous methods can access related local variables and parameters.
Iterators are methods that can incrementally calculate and produce values. iterators let a type specify how the foreach statement iterates over all its elements and becomes easy.
Incomplete types allow classes, structs, and interfaces to be split into multiple parts stored in different source files, which is more conducive to development and maintenance. In addition, the incomplete type allows some types of machine-generated parts to be detached from the user-written part, so it is easy to add code generated by the tool.


These new features are described in this chapter. After the introduction, the next four chapters provide complete technical specifications for these features.

C#2.0 's language extensions are primarily designed to ensure maximum compatibility with existing code. For example, although c#2.0 gives special meaning to the terms of where, yield, and partial in specific contexts, these words can still be used as identifiers. In fact, C # 2.0 does not add any keywords that might conflict with identifiers in existing code.

19.1 generic type

Generics enable classes, structs, interfaces, delegates, and methods to be parameterized by the type of data they store and manipulate. C # Generics are familiar to users of generics using Eiffel or ADA, or to users of C + + templates, but they will not have to endure the many complexities of the latter.


19.1.1 Why generics are used

Without generics, a general purpose data structure can store any type of information using the object type. For example, the following stack class stores data in an object array, and its two methods, push and pop, use object to receive and return data accordingly.

public class stack{object[] items;int count;public void Push (Object item) {...} public Object Pop () {...}}

Although using type object can make the stack class more flexible, it is not without drawbacks. For example, you can press a value of any type, such as an instance of customer, into the (Push) stack. But when you retrieve a value, the result of the Pop method must be explicitly cast to the appropriate type, which is annoying for a run-time type check to write code, and the performance disadvantage.

Stack stack = new stack (); Stack.push (New Customer ()); Customer C = (customer) stack. Pop ();

If a value of type value, such as an int is passed to the push method, it will be automatically boxed. When this int is obtained later, it must be unboxed with an explicit cast.

Stack stack = new stack (); Stack.push (3); int I = (int) stack. Pop ();

This boxing and unboxing operation increases the performance overhead because they involve dynamic memory allocation and run-time type checking.

The bigger problem with the stack class is that it cannot force the kind of data that is placed on the stack. In fact, a customer instance can be pressed into the stack, and it may be cast to the wrong type when it is retrieved.

Stack stack = new stack (); Stack.push (New Customer ()); string s = (string) stack. Pop ();

Although the previous code is an inappropriate use of the stack class, this code is technically correct and does not report compile-time errors. The problem is not popping up until the code executes, and a InvalidCastException exception will be thrown at this point.

If the stack class has the ability to specify the type of its elements, it is clear that it can benefit from this capability. Using generics, this will become possible.

19.1.2 Creating and using generics

Generics provide a tool for creating types that have type parameters (type parameter). The following example declares a generic stack class with type-parameter T. Type parameters are specified in the "<" and ">" delimiters after the class name. There is no mutual conversion between object and other types,stack<t> instances accept the type they were created in, and store that type of data without converting it. The type parameter T acts as a placeholder until it is used to specify an actual type. Note that T is used as the element type of the internal items array, the type of the push method parameter, and the return value type of the Pop method.

public class stack<t>{t[] items;int count;public void Push (T item) {...} Public T Pop () {...}}

When a generic class stack<t> is used, the actual type in which T is substituted is specified. In the example below, int will be given as the type parameter of T.

stack<int> stack = new stack<int> (); Stack.push (3); int x = stack. Pop ();

The stack<int> type is called the constructed type (constructed type). In the stack<int> type, each occurrence of T is replaced with the type parameter int. When an instance of stack<int> is created, the local storage of the items array is a int[] instead of object[], which provides higher storage efficiency than a non-generic stack. Similarly, the push and pop methods of the stack<int> operation on the int value will cause a compile-time error to be pressed into the value of the other type, and no need to convert back to their original type when retrieving the value.

Generics provide a strong type, meaning such as pressing an int into the customer object stack will cause an error. As if stack<int> were restricted to operate on int values only, the same stack<customer> is also limited to the Customer object.

For the following example, the compiler will report an error on the last two lines.

stack<customer> stack = new stack<customer> (); Stack.push (New Customer ()); Customer C = Stack. Pop (); stack. Push (3); Type mismatch error int x = stack. Pop (); Type mismatch error

A generic type declaration can have any number of type parameters. The previous stack<t> example has only one type parameter, but a common dictionary class may have two type parameters, one for the key (keys), and the other for the value type.

public class dictionary<k, V>{public void Add (K key, V value) {...} Public V this[k key]{...}} When Dictionary<k, v> is used, it must provide two types of parameters. dictionary<string, customer> dict = new dictionary<string, customer> ();D ICT. ADD ("Peter", New Customer ()); Custeomer C = dict["Perter"];

19.1.3 generic type instantiation

Like non-generic types, compiled generic types are also represented by intermediate language [intermediate Language (IL)] directives and metadata. The representation of a generic type, of course, also encodes the existence and use of a type parameter.

When an application first creates an instance of a constructed generic type, for example, Stack<int> The net common language runtime's real-time compiler (JIT) converts the generic IL and metadata to local code in the process, and replaces the type parameter with the actual type. Subsequent references to that constructed generic type will use the same native code. A procedure for creating a specific constructed type from a generic type, called a generic type instantiation (generic type instantiation). [/b]

. The net common language runtime uses value types to create a specific copy of local code for each instance of a generic type, but for all reference types it will share a single copy of that local code (because, at the local code level, the reference is simply a pointer with the same representation).

19.1.4 constraints

In general, generic classes are not limited to simply storing values based on type parameters. Generic classes can often invoke methods on objects of the type of a given type parameter. For example, Dictionary<k, the Add method in the V> class may need to compare key values using the CompareTo method.

public class dictionary<k, V>{public void Add (K key, V value) {... if (Key.compareto (x) <0) {...} Error, no CompareTo method ...}}

Because the type parameter specified for k may be of any type, the only member that can be assumed to exist for the key parameter is those declared as type object, such as Equals,gethashcode and ToString, so a compile-time error will occur in the previous example. Of course, you can cast the key parameter to a type that contains the CompareTo method. For example, the key parameter may be cast to the IComparable interface.

public class dictionary<k, V>{public void Add (K key, V value) {... if (((IComparable) key). CompareTo (x) <0) {...} ...}}

Although this workaround works, it requires dynamic type checking at run time, which also increases the overhead. Worse, it defers the error report to the runtime, and throws a InvalidCastException exception if the key (key) does not implement the IComparable interface.

In order to provide stronger compile-time type checking and reduce type casts, C # allows you to provide an optional list of constraints (constraint) for each type parameter. A type parameter constraint specifies a requirement that a type must fulfill in order to be used as an argument (argument) for a type parameter. Constraints use the word where declaration, followed by the name of the type parameter, followed by a list of class or interface types, and an optional constructor constraint, new ().

public class dictionary<k, v> where k:icomparable{public void Add (K key, V value) {... if (Key.compareto (x) <0) {...} ...}}

Given this declaration, the compiler will ensure that any type argument of K is the type that implements the IComparable interface.

Also, it is no longer necessary to explicitly cast the key parameter before calling the CompareTo method. All members of a type given as a constraint for a type parameter are directly valid for values of type parameter types.

For a given type parameter, you can specify any number of interfaces as constraints, but only one class. The type parameter for each constraint has a separate where statement. In the following example, the type parameter K has two interface constraints, and the type parameter E has a class constraint and a constructor constraint.

public class entitytable<k, E>where k:icomparable<k>,ipersisablewhere e:entity, new () {public void Add (K key , E entity) {... if (Key.compareto (x) <0) {...} ...}}

In the preceding example, the constructor constrains new () to ensure that the type used as the type parameter has a public, parameterless constructor, and that it allows the generic class to create an instance of the type using new E ().

Type parameter constraints should be used with caution. Although they provide stronger compile-time type checking and, in some cases, performance enhancements, they also limit the possible use of generic types. For example, the generic class list<t> may constrain T to implement the IComparable interface, so its sort method will be able to compare the size of the item. However, this makes it impossible for a type that does not implement the IComparable interface to use LIST<T>, even in these cases, the sort method is not called at all.

The above is C # 2.0 specification (a) Introduction of the content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!


  • Related Article

    Contact Us

    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.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.