C # 2.0 Specification (1) Introduction

zhaozj2021-02-16  47

This article is a Microsoft's technical article for translation. For reference for learning C #, please do not use for commercial purposes. http://msdn.microsoft.com/vcsharp/team/language/default.aspx

19.c # 2.0 Introduction

C # 2.0 Introduces several language extensions, which are generic, anonymous methods, iterators, and incomplete types (Partial Type).

Generics allows classes, structures, interfaces, commissions, and methods to be parameterized by the type of data they store and manipulated. The generic is very useful because they provide stronger compile time type checks, reducing explicit conversions between data types, as well as packing operations and runtime types.

An anonymous method allows code blocks to sneak into places where the expected entrusted value is in line. The anonymous method is similar to the lambda function in the LISP programming language. C # 2.0 supports the creation of "Closures", where the anonymous method can access the relevant local variables and parameters.

The iterator is a method of incrementing calculation and generating values. The iterator makes the type specifies how the Foreach statement iterates all the elements of it, it is easy.

Incomplete types allow class, structural, and interfaces to be split into multiple parts stored in different source files, which is more beneficial to development and maintenance. In addition, the incomplete type allows some types of machines to be separated from parts that are written in the user, so it is easy to increase the code generated by the tool.

This chapter will introduce these new features. After the introduction, the next four chapters provide the complete technical specifications for these features.

The language extension of C # 2.0 is mainly designed to ensure maximum compatibility between existing code. For example, although C # 2.0 gives the words for WHERE, YIELD and Partial, these words can still be used as identifiers. In fact, C # 2.0 does not add any keywords that may conflict with identifiers in existing code.

19.1 generics

Generics allows classes, structures, interfaces, commissions, and methods to be parameterized by the type of data they store and manipulated. C # generics is familiar for using Eiffel or ADA's generic users, or for users of C templates; but they will not have to endure the many complexities of the latter.

19.1.1 Why use a generic

If there is no generic, the data structure of the general purpose can use the Object type to store any type of data. 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.

Public Class Stack

{

Object [] items;

INT country;

Public void push (Object item) {...}

Public Object Pop () {...}

}

Although the type Object can make the Stack class more flexible, do not have a shortcomings. For example, you can press a value, such as an instance of Customer, insert (Push) stack. However, when you retrieve a value, the result of the POP method must be explicitly converted to the appropriate type, checking the code for a runtime type, and is very annoying.

Stack stack = new stack ();

Stack.push (New Customer ());

Customer C = (Customer) stack.pop ();

If a value of a value type, for example, an int is passed to the Push method, it will be automatically contained. When this INT is obtained later, it must be removed using an explicit forced conversion.

Stack stack = new stack ();

STACK.PUSH (3);

INT i = (int) stack.pop ();

This packing and unloading operation add performance overhead because they involve dynamic memory allocation and runtime type checks. The bigger problem with the Stack class is that it cannot force the data types on the stack. In fact, the Customer instance can be pressed into the stack, and may be forced to convert to the wrong type when retrieving it.

Stack stack = new stack ();

Stack.push (New Customer ());

STRING S = (String) stack.pop ();

Although the previous code is an inappropriate usage of the Stack class, this code is technically correct, and it will not report errors when compiling. The problem will come out until the code is executed, and an InvalidCastException exception will be thrown at this point.

If the Stack class has the ability to specify its element, it is clear that it can be beneficial from this capability. This will become possible using a generic.

19.1.2 Creating and using generics

The generics provides tools for creating types with type parameters. The following example declares a generic STACK class with type parameter T. Type parameters are specified in the <"and"> "dividing character after class name. There is no other conversion between Object and other types, and instances of Stack accept the types they are created, and store the type of data without converting it. The type parameter T acts as a placeholder until it specifies an actual type when it is used. Note that T is used as an element type of an internal Items array, a type of PUSH method parameter, and a return value type of the POP method.

Public Class Stack

{

T [] items;

INT country;

Public void push (t item) {...}

Public T Pop () {...}

}

When the generic class Stack is used, the actual type instead of T will be specified. In the example below, INT will be given as a type T type parameter.

Stack stack = new stack ();

STACK.PUSH (3);

INT x = stack.pop ();

Stack is called constructed type. In the Stack type, each appearance of T is replaced by the type parameter INT. When the instance of Stack is created, the local storage of the Items array is int [] rather than Object [], which provides higher storage efficiency compared to non-float STACK. Similarly, the PUSH and POP methods of Stack on the int value, will make a compile error in the stack in the stack, and do not need to convert them when the value is taken Original type.

The generic type provides a strong type, such as, for example, embraced an int to Customer object stack will have an error. It seems that STACK is limited to operate on the int value, the same stack is also limited to the Customer object.

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

Stack Stack = New Stack ();

Stack.push (New Customer ());

Customer c = stack.pop ();

Stack.push (3); // type does not match errors

INT x = stack.pop (); // type does not match errors

The generic type declaration can have any number of types of parameters. Previous Stack example only has a type of parameters, but a universal Dictionary class may have two types of parameters, one type for key (key), another type for value (value). Public Class Dictionary

{

Public Void Add (K Key, V Value) {...}

Public v this [k Key] {...}

}

When Dictionary is used, two types of parameters must be provided.

Dictionary DICT = New Dictionary ();

Dict.Add ("Peter", New Customer ());

Custeomer C = DICT ["peter"];

19.1.3 Instantization of generic types

Similar to the non-float type, the compiled generic type is also indicated by the intermediate language [Intermediate Language (IL)] instruction and metadata. The generic type of generic type is of course encoding the existence and use of type parameters.

When the application creates an instance of a generic type, for example, Stack , the real-time compiler (JIT) at the NET public language runtime will convert generic IL and metadata to a local code in the process. And replace the type parameters to actual types. The same native code will be used for subsequent references that constructive generic types. Creating a specific constructor from a generic type, called generic type instantiation.

.NET public language runtime use value type creates a specific copy of a local code for each generic type instance, but for all reference types, it will share a single copy of local code (because of the local code level, reference It is just a pointer with the same representation).

19.1.4 constraint

Generally speaking, generic classes are not limited to storage values ​​based on type parameters. Pancases often call methods on objects of a given type parameter. For example, the Add method in the Dictionary class may need to use the CompareTo method to compare the key value.

Public Class Dictionary

{

Public Void Add (K Key, V Value)

{

...

IF (Key.Compareto (x) <0) {...} // error, no Compareto method

...

}

}

Because the type parameters specified for K may be any type, it is assumed that the only member of the Key parameter is those that are declared object type, for example, equals, gethashcode and torstring; therefore, when compiling in the previous example error. Of course, you can force the Key parameter to a type containing the CompareTo method. For example, the Key parameter may be forced to convert to the iComparable interface.

Public Class Dictionary

{

Public Void Add (K Key, V Value)

{

...

IF ((iComparable) key) .compareto (x) <0) {...}

...

}

}

Although this solution is valid, it needs to perform dynamic type check at runtime, which has also increased overhead. Worse, it will post the error report to run, if the key (key) does not implement the ICOMPARABLE interface will throw an InvalidCastException exception.

In order to provide a stronger compile time type check, and reduce type forced conversion, C # allows each type of parameters to provide an optional list of constraints. Type parameter constraints specify a need for a type that must be fulfilled, and its purpose is to be used as an argument for type parameters. Constraints WHERE declarations, followed by the name of the type parameters, followed by a list of class or interface type, and optional constructor constraints new ().

Public Class Dictionary Where k: iComparable

{

Public Void Add (K Key, V Value)

{

...

IF (Key.Compareto (x) <0) {...}

...

}

}

Given this statement, the compiler will ensure that any type of collections of K are the type of ICOMPARABLE interface.

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

For a given type parameter, you can specify any number of interfaces as constraints, but only one class. The type parameters of each constraint have 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

WHERE K: IComparable , IPERSISABLE

WHERE E: E: E: E: E: E: E: E: E: E: E: E: E: E: E: E:

{

Public Void Add (K Key, E Entity)

{

...

IF (Key.Compareto (x) <0) {...}

...

}

}

In the previous example, the function constraints NEW () ensures that the type e used as the type of type parameters has a public, no parameter constructor, and it allows the generic class to create this type of instance using new E ().

Type parameter constraints should be very careful. Although they provide stronger compile time type checking, in some cases enhanced performance, they also limit possible usage of generic types. For example, generic class List may constrain T Implement the IComparable interface, where it will be able to compare the size of the item. However, this is made so that the type of ICOMPARABLE interface cannot be used without using List , even in these situations, the sort method is not called at all.

转载请注明原文地址:https://www.9cbs.com/read-25908.html

New Post(0)