Microsoft translation of free book "Introducing Microsoft LINQ" chapter2.0: C # Language Features

Source: Internet
Author: User
Document directory
  •  
This book is intended for personal learning and knowledge sharing. The copyright of this book is owned by the original author. If there is any infringement, please inform me that I will handle the post immediately.
Reprinting is allowed, but this copyright statement is not allowed for commercial purposes!
Blog: Han xilong

Introducing to Microsoft LINQ directory

You do not have to learn how to fully understand the enhancements of C #3.0. For example, none of the new language features of (LINQ) Involve CLR changes. The new Compiler (C #3.0 or Microsoft Visual Basic 9.0) is required for LINQ. The intermediate code generated by these compilers can be used in Microsoft.. NET 2.0.

However, in this chapter, we have compiled a concise description of the C # features that you need to understand and use in LINQ (from C # 1.x to C #3.0 ). If you want to skip this chapter, you can go back to this chapter when you want to understand what is going on inside the LINQ syntax.

Review C #2.0

C #2.0 improved the original C # language in many aspects. For example, the use of generic allows developers to use C # To define methods and classes with one or more type parameters. Generic is a pillar of LINQ.

In this chapter, we will describe some C #2.0 syntaxes that are very important to LINQ: Generic (generics), anonymous (anonymous methods, they are the basis of lambda expressions in C #3.0), yield keywords, and APIs that can be enumerated. To have a better understanding of these concepts, you must have a better understanding of these concepts.

Generic)

Many programming languages Process Variables and objects by defining specific types and strict conversion rules. The code written in a strong language lacks the generalization conditions. Consider the following code:

int Min( int a, int b ) {if (a < b) return a;else return b;}

To use this code, we need a different min version containing each type of parameter for comparison. Developers who are used to using objects as the default type of generic may write the following method:

object Min( object a, object b ) {if (a < b) return a;else return b;}

Unfortunately, the (<) operator between common object types cannot be used. We need to use a common interface for processing:

IComparable Min( IComparable a, IComparable b ) {if (a.CompareTo( b ) < 0) return a;else return b;}

However, even if we solve this problem, we will face a bigger problem: the result type of this min method is uncertain. Calling min can convert the icomparable type to the int type, but it may cause exceptions and will definitely increase the CPU usage cost.

int a = 5, b = 10;int c = (int) Min( a, b );

C #2.0 uses generic to solve this problem. The basic principle of generic is to transfer type conversion from C # compiler to CLR jitter for processing. Below is the generic version of the min method:

T Min<T>( T a, T b ) where T : IComparable<T> {if (a.CompareTo( b ) < 0) return a;else return b;}

[Tips]

Jitter is the real-time Compilation part of. Net runtime. It translates the Il code into a machine code. When you compile. Net code, the compiler will generate the Il code, and then compile the Il code into the machine code through jitter before the first processing.

Converting type processing to jitter is a good method: Jitter can generate multiple versions of the same Code, one of which can be used. This method is similar to a macro extension. The difference is that this optimization is used to avoid code spread, that is, the generic method) all versions use the reference type as the generic (generics) type and share the same compiled code, only the calling method is different.

Use the generic method to write the following code:

Int A = 5, B = 10;

Int c =(INT) min (A, B );

You can write the following code:

Int A = 5, B = 10;

Int c = min <int> (A, B );

The problem is gone, and the code runs faster than before. In addition, the compiler can infer from the parameter that the min method of the T-type generic (generics) is used, so we can write the following code:

Int A = 5, B = 10;

Int c = min (A, B );

Type inference)

Type inference is a key feature. It allows you to write more abstract code and write the abstract code so that the compiler can process the details about the type. However, the C # type conversion mechanism cannot ensure that all types are correct during compilation, nor can it intercept Error Code (for example, when calling completely incompatible types ).

Generic methods can not only define generic methods, but also use type Declarations like classes and interfaces. As mentioned above, this book does not aim to explain in detail the wildcard (generics), but to remind you that the combination of generic (generics) and LINQ will be very comfortable.

Delegate)

A delegate is a class that encapsulates one or more methods. Internally, a proxy stores the pointer list of some methods. Each pointer corresponds to a class containing the instance method.

A single delegate can contain several methods, but in this chapter we only discuss the delegate that contains one method ). Abstract: The delegate type is like a code container. The code in the container cannot be changed, but it can be called independently by the stack or stored as a variable. It stores an instance object, so that the lifecycle of the object can be extended until the delegate is used effectively.

The evolution of the delegate syntax is the basis of the anonymous method, which will be discussed in the next chapter. Declaring a delegate is actually a type that can be instantiated. The delegate (delegate) Statement requires a complete method signature. In Listing 2-1, we declare three different types: each of them with the same method signature can only be instantiated through an external method.

Listing 2-1: Delegate Declaration

Delegate void simpledelegate ();

Delegate int returnvaluedelegate ();

Delegate void twoparamsdelegate (string name, int age );

The delegate (delegate) is a previously upgraded security version of the C function pointer. Using C # 1.x, a delegate can be generated only by explicitly creating an object, as shown in listing 2-2:

Listing 2-2: Delegate instantiation (C # 1.x)

Public class demodelegate {

Void methoda (){... }

Int methodb (){... }

Void methodc (string X, int y ){... }

Void createinstance (){

Simpledelegate A = new simpledelegate (methoda );

Returnvaluedelegate B = new returnvaluedelegate (methodb );

Twoparamsdelegate c = new twoparamsdelegate (methodc );

//...

}

}

Using the original syntax to create a delegate is boring: even if the current type is stronger than the type to be delegated, you still have to know the name of the delegate class, because no other types are allowed. In this way, the delegate type can be used safely ..

C #2.0 takes this into consideration and allows you to skip that part of the syntax. We can see that the delegated instance can be created without the new keyword. You only need to know the method name. The compiler will deduce the type of the delegate. If you specify a variable simpledelegate type, the C # compiler automatically generates a new simpledelegate Code, which applies to any delegate) type. The Code of C #2.0 and C #1. x displayed in Listing 2-3 generates the same il code.

Listing 2-3: Delegate instantiation (C #2.0)

Public class demodelegate {

Void methoda (){... }

Int methodb (){... }

Void methodc (string X, int y ){... }

Void createinstance (){

Simpledelegate A = methoda;

Returnvaluedelegate B = methodb;

Twoparamsdelegate c = methodc;

//...

}

//...

}

You can also customize a generic delegate type. It is obvious that defining a delegate in a generic class is a very important and useful function for LINQ.

A common point of delegation is to inject some code into an existing method. In Listing 2-4, we assume there is a method named repeat10times that we don't want to change.

Listing 2-4: common use for a delegate

Public Class Writer {

Public String text;

Public int counter;

Public void dump (){

Console. writeline (text );

Counter ++;

}

}

Public class demodelegate {

Void repeat10times (simpledelegate somework ){

For (INT I = 0; I <10; I ++) somework ();

}

Void run1 (){

Writer writer = new writer ();

Writer. Text = "C # chapter ";

This. repeat10times (writer. Dump );

Console. writeline (writer. Counter );

}

//...

}

The currently used callback function is defined as simpledelegate. We want to inject a string to measure how many times this method has been called. Defines a writer class and uses the instantiated data as a parameter of the dump method. As you can see, we need to define a separate class to only store the code and data we need. A shortcut is similar but anonymous (anonymous method ).

Anonymous method (anonymous methods)

In the previous chapter, we mentioned the sharing of delegation. In C #2.0, there is a code writing method, that is, the anonymous method (anonymous method) is concise in Listing 2-4 ). See the Listing 2-5 example.

Listing 2-5: using an anonymous method

Public class demodelegate {

Void repeat10times (simpledelegate somework ){

For (INT I = 0; I <10; I ++) somework ();

}

Void run2 (){

Int counter = 0;

This. repeat10times (delegate {

Console. writeline ("C # chapter ");

Counter ++;

});

Console. writeline (Counter );

}

//...

}

In the code, we do not declare the writer class. The compiler automatically declares a class for us and calls its name. Instead, we define a method to call repeat10times. It seems that we are using this part of code as a parameter. However, the compiler converts the code into a common delegate that is similar to a specific class. In code, the only proof of such conversion is the keyword before the code block. This syntax is called the anonymous method ).

[Tips]

Remember that you cannot pass the code into a variable, but only a pointer can be passed in. Before proceeding, please confirm that you have not made a mistake.

The anonymous method (anonymous method) marked as the delegate keyword before the code segment ). When we have a delegate method that contains one or more parameters, this syntax allows us to define the parameter name as a delegate ). The code in Listing 2-6 defines an anonymous method of the delegate type as twoparamsdelegate.

Listing 2-6: parameters for an anonymous method

Public class demodelegate {

Void repeat10times (twoparamsdelegate callback ){

For (INT I = 0; I <10; I ++) callback ("LINQ book", I );

}

Void run3 (){

Repeat10times (delegate (string text, int age ){

Console. writeline ("{0} {1}", text, age );

});

}

//...

}

We use two explicit parameters as the delegate to replace the repeat10times method. It can be understood as follows: if you remove the declaration of the text and age parameters, the Delegate will generate two errors with the name defined.

Key Points

You will use delegate and anonymous methods in C #3.0 (indirectly). It is important to have a deep understanding of these concepts for this reason. Only in this way can you grasp it from an abstract height in the face of increasingly complex situations.

Enumerators and yield keywords

C # 1.x defines two interfaces to support enumeration. The namespace system. Collections contains these declarations, as shown in Listing 2-7:

Listing 2-7: ienumerator and ienumerable declarations

public interface IEnumerator {bool MoveNext();object Current { get; }void Reset();}public interface IEnumerable {IEnumerator GetEnumerator();}

You can enumerate the objects that instantiate the ienumerator interface. This enumeration can call the movenext method until it returns false.

The code in Listing 2-8 defines a class in this way. As you can see, the countdownenumerator class is more complex and implements the enumeration logic separately. In this example, we do not really want to enumerate anything, but simply return the descending order number starting from the startcountdown number defined in the countdown class. The countdown class is also an enumeration class.

Listing 2-8: enumerable class

public class Countdown : IEnumerable {public int StartCountdown;public IEnumerator GetEnumerator() {return new CountdownEnumerator( this );}}public class CountdownEnumerator : IEnumerator {private int _counter;private Countdown _countdown;public CountdownEnumerator( Countdown countdown ) {_countdown = countdown;Reset();}public bool MoveNext() {if (_counter > 0) {_counter--;return true;}else {return false;}}public void Reset() {_counter = _countdown.StartCountdown;}public object Current {get {return _counter;}}}

Countdownenumerator is used only when the delegate actually occurs. For example, listing 2-9 shows a possible usage.

Listing 2-9: sample enumeration code

public class DemoEnumerator {public static void DemoCountdown() {Countdown countdown = new Countdown();countdown.StartCountdown = 5;        IEnumerator i = countdown.GetEnumerator();        while (i.MoveNext()) {int n = (int) i.Current;Console.WriteLine( n );}        i.Reset();        while (i.MoveNext()) {int n = (int) i.Current;Console.WriteLine( "{0} BIS", n );}}// …}

You can call getenumerator to provide enumeration objects. We use two loops to display the reset method usage. We have to convert the current return value to int type because we use a non-generic enumeration interface.

[Tips] C #2.0 introduces the support for generic enumeration. The namespace system. Collections. Generic contains the declaration of ienumerable and ienumerator. These interfaces can convert data to the object type without conversion. For the enumerated value type, this capability is very important, because it may not affect the packing or unpacking operation.

Since C # 1.x, you can use foreach for enumeration. The results shown in the code in Listing 2-10 are the same as those in the previous example.

Listing 2-10: enumeration using a foreach statement

public class DemoEnumeration {public static void DemoCountdownForeach() {Countdown countdown = new Countdown();countdown.StartCountdown = 5;        foreach (int n in countdown) {Console.WriteLine( n );}        foreach (int n in countdown) {Console.WriteLine( "{0} BIS", n );}}// …}

With foreach, the compiler calls movenext before each loop after initializing getenumerator. The real difference is that each loop generates code without calling the reset method: Two countdownenumerator objects instead of one object are generated.

[Tips] foreach can also be used for classes that do not contain the ienumerable interface but have a common enumeration method (getenumerator method.

C #2.0 use the compiler to automatically generate a class inherited from the ienumerator interface and returned the getenumerator method to describe yield. Yield can only be used before the return or break keywords. The Listing 2-11 code generates a class equivalent to the countdownenumerator described earlier.

Listing 2-11: enumeration using a yield statement

public class CountdownYield : IEnumerable {public int StartCountdown;public IEnumerator GetEnumerator() {for (int i = StartCountdown - 1; i >= 0; i--) {            yield return i;}}}

 

Logically, yield return is equivalent to suspending execution before the next movenext is called ). Recall that the getenumerator method is called only once throughout the enumeration. It inherits from the ienumerator interface and returns a class. The class inherits the declarations from the methods that contain yield.

A method that includes yield is called an iterator. An iterator can contain multiple yield instances. The code in Listing 2-12 is absolutely valid and functionally equivalent to the countdownyield class with the startcountdown value of 5.

Listing 2-12: Multiple yield statements

public class CountdownYieldMultiple : IEnumerable {public IEnumerator GetEnumerator() {        yield return 4;        yield return 3;        yield return 2;        yield return 1;        yield return 0;}}

By using the ienumerator generic type, you can define a strong countdownyield type, as shown in Listing 2-13.

Listing 2-13: enumeration Using yield (typed)

public class CountdownYieldTypeSafe : IEnumerable<int> {public int StartCountdown;    IEnumerator IEnumerable.GetEnumerator() {        return this.GetEnumerator();    }    public IEnumerator<int> GetEnumerator() {for (int i = StartCountdown - 1; i >= 0; i--) {yield return i;}}}

This strong-type version contains two getenumerator Methods: one is compatible with non-Generic Code (returning ienumerable), and the other is strongly typed (returning ienumerator <int> ).

Enumeration and yield are widely used in LINQ. Even if they are encapsulated, you should think of them when debugging code.

Translator: blog garden temptation

School Draft: Han xilong, blog Park

 

Previous Article: Microsoft free book "Introducing Microsoft LINQ" Translation-chapter1.5 and chapter1.6: Current Situation and Prospects of LINQ

Next article: C #3.0 features

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.