Apress pro LINQ Language Integrated Query in C# 2008 phần 2 ppsx

75 511 0
Apress pro LINQ Language Integrated Query in C# 2008 phần 2 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ 33 This could even be rewritten as the following: Enumerable enumerable = {"one", "two", "three"}; Enumerable finalEnumerable = enumerable .Where(lX1) .Where(lX2) .Where(lX3); Wow, that’s much easier to read. You can now read the statement from left to right, top to bottom. As you can see, this syntax is very easy to follow once you understand what it is doing. Because of this, you will often see LINQ queries written in this format in much of the LINQ documentation and in this book. Ultimately what you need is the ability to have a static method that you can call on a class instance. This is exactly what extension methods are and what they allow. They were added to C# to provide a syntactically elegant way to call a static method without having to pass the method’s first argument. This allows the extension method to be called as though it were a method of the first argu- ment, which makes chaining extension method calls far more readable than if the first argument was passed. Extension methods assist LINQ by allowing the Standard Query Operators to be called on the IEnumerable<T> interface. ■Note Extension methods are methods that while static can be called on an instance (object) of a class rather than on the class itself. Extension Method Declarations and Invocations Specifying a method’s first argument with the this keyword modifier will make that method an extension method. The extension method will appear as an instance method of any object with the same type as the extension method’s first argument’s data type. For example, if the extension method’s first argument is of type string, the extension method will appear as a string instance method and can be called on any string object. Also keep in mind that extension methods can only be declared in static classes. Here is an example of an extension method: namespace Netsplore.Utilities { public static class StringConversions { public static double ToDouble(this string s) { return Double.Parse(s); } public static bool ToBool(this string s) { return Boolean.Parse(s); } } } Notice that both the class and every method it contains are static. Now you can take advantage of those extension methods by calling the static methods on the object instances as shown in Listing 2-15. Because the ToDouble method is static and its first argument specifies the this keyword, ToDouble is an extension method. Rattz_789-3C02.fm Page 33 Tuesday, October 16, 2007 2:19 PM 34 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ Listing 2-15. Calling an Extension Method using Netsplore.Utilities; double pi = "3.1415926535".ToDouble(); Console.WriteLine(pi); This produces the following results: 3.1415926535 It is important that you specify the using directive for the Netsplore.Utilities namespace, otherwise the compiler will not find the extension methods and you will get compiler errors such as the following: 'string' does not contain a definition for 'ToDouble' and no extension method 'ToDouble' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) As mentioned previously, attempting to declare an extension method inside a nonstatic class is not allowed. If you do so, you will see a compiler error like the following: Extension methods must be defined in a non-generic static class Extension Method Precedence Normal object instance methods take precedence over extension methods when their signature matches the calling signature. Extension methods seem like a really useful concept, especially when you want to be able to extend a class you cannot, such as a sealed class or one for which you do not have source code. The previous extension method examples all effectively add methods to the string class. Without exten- sion methods, you couldn’t do that because the string class is sealed. Partial Methods Recently added to C# 3.0, partial methods add a lightweight event-handling mechanism to C#. Forget the conclusions you are more than likely drawing about partial methods based on their name. About the only thing partial methods have in common with partial classes is that a partial method can only exist in a partial class. In fact, that is rule 1 for partial methods. Before I get to all of the rules concerning partial methods let me tell you what they are. Partial methods are methods where the prototype or definition of the method is specified in the declaration of a partial class, but an implementation for the method is not provided in that same declaration of the partial class. In fact, there may not be any implementation for the method in any declaration of that same partial class. And if there is no implementation of the method in any other declaration for the same partial class, no IL code is emitted by the compiler for the declaration of the method, the call to the method, or the evaluation of the arguments passed to the method. It’s as if the method never existed. Rattz_789-3C02.fm Page 34 Tuesday, October 16, 2007 2:19 PM CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ 35 Some people do not like the term “partial method” because it is somewhat of a misnomer due to their behavior when compared to that of a partial class. Perhaps the method modifier should have been ghost instead of partial. A Partial Method Example Let’s take a look at a partial class containing the definition of a partial method in the following class file named MyWidget.cs: The MyWidget Class File public partial class MyWidget { partial void MyWidgetStart(int count); partial void MyWidgetEnd(int count); public MyWidget() { int count = 0; MyWidgetStart(++count); Console.WriteLine("In the constructor of MyWidget."); MyWidgetEnd(++count); Console.WriteLine("count = " + count); } } In the MyWidget class declaration above, I have a partial class named MyWidget. The first two lines of code are partial method definitions. I have defined partial methods named MyWidgetStart and MyWidgetEnd that each accept an int input parameter and return void. It is another rule that partial methods must return void. The next piece of code in the MyWidget class is the constructor. As you can see, I declare an int named count and initialize it to 0. I then call the MyWidgetStart method, write a message to the console, call the MyWidgetEnd method, and finally output the value of count to the console. Notice I am incre- menting the value of count each time it is passed into a partial method. I am doing this to prove that if no implementation of a partial method exists, its arguments are not even evaluated. In Listing 2-16 I instantiate a MyWidget object. Listing 2-16. Instantiating a MyWidget MyWidget myWidget = new MyWidget(); Let’s take a look at the output of this example by pressing Ctrl+F5: In the constructor of MyWidget. count = 0 As you can see, even after the MyWidget constructor has incremented its count variable twice, when it displays the value of count at the end of the constructor, it is still 0. This is because the code for the evaluation of the arguments to the unimplemented partial methods is never emitted by the compiler. No IL code was emitted for either of those two partial method calls. Now let’s add an implementation for the two partial methods: Rattz_789-3C02.fm Page 35 Tuesday, October 16, 2007 2:19 PM 36 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ Another Declaration for MyWidget but Containing Implementations for the Partial Methods public partial class MyWidget { partial void MyWidgetStart(int count) { Console.WriteLine("In MyWidgetStart(count is {0})", count); } partial void MyWidgetEnd(int count) { Console.WriteLine("In MyWidgetEnd(count is {0})", count); } } Now that you have added this declaration, run Listing 2-16 again and look at the results: In MyWidgetStart(count is 1) In the constructor of MyWidget. In MyWidgetEnd(count is 2) count = 2 As you can see, not only are the partial method implementations getting called, the arguments passed are evaluated as well. You can see this because of the value of the count variable at the end of the output. What Is the Point of Partial Methods? So you may be wondering, what is the point? Others have said, “This is similar to using inheritance and virtual methods. Why corrupt the language with something similar?” To them I say “Take a chill-pill Jill.” Partial methods are more efficient if you plan on allowing many potentially unimple- mented hooks in the code. They allow code to be written with the intention of someone else extending it via the partial class paradigm but without the degradation in performance if they choose not to. The case in point for which partial methods were probably added is the code generated for LINQ to SQL entity classes by the entity class generator tools. To make the generated entity classes more usable, partial methods have been added to them. For example, each mapped property of a gener- ated entity class has a partial method that is called before the property is changed and another partial method that is called after the property is changed. This allows you to add another module declaring the same entity class, implement these partial methods, and be notified every time a property is about to be changed and after it is changed. How cool is that? And if you don’t do it, the code is no bigger and no slower. Who wouldn’t want that? The Rules It has been all fun and games up to here, but unfortunately, there are some rules that apply to partial methods. Here is a list: • Partial methods must only be defined and implemented in partial classes • Partial methods must specify the partial modifier • Partial methods are private but must not specify the private modifier or a compiler error will result Rattz_789-3C02.fm Page 36 Tuesday, October 16, 2007 2:19 PM CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ 37 • Partial methods must return void • Partial methods may be unimplemented • Parital methods may be static • Partial methods may have arguments These rules are not too bad. For what we gain in terms of flexibility in the generated entity classes plus what we can do with them ourselves, I think C# has gained a nice feature. Query Expressions One of the conveniences that the C# language provides is the foreach statement. When you use foreach, the compiler translates it into a loop with calls to methods such as GetEnumerator and MoveNext. The simplicity the foreach statement provides for enumerating through arrays and collections has made it very popular and often used. One of the features of LINQ that seems to attract developers is the SQL-like syntax available for LINQ queries. The first few LINQ examples in the first chapter of this book use this syntax. This syntax is provided via the new C# 3.0 language enhancement known as query expressions. Query expressions allow LINQ queries to be expressed in nearly SQL form, with just a few minor deviations. To perform a LINQ query, it is not required to use query expressions. The alternative is to use standard C# dot notation, calling methods on objects and classes. In many cases, I find using the standard dot notation favorable for instructional purposes because I feel it is more demonstrative of what is actually happening and when. There is no compiler translating what I write into the standard dot notation equivalent. Therefore, many examples in this book do not use query expression syntax but instead opt for the standard dot notation syntax. However, there is no disputing the allure of query expression syntax. The familiarity it provides in formulating your first queries can be very enticing indeed. To get an idea of what the two different syntaxes look like, Listing 2-17 shows a query using the standard dot notation syntax. Listing 2-17. A Query Using the Standard Dot Notation Syntax string[] names = { "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland", "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield", "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson", "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley", "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft", "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"}; IEnumerable<string> sequence = names .Where(n => n.Length < 6) .Select(n => n); foreach (string name in sequence) { Console.WriteLine("{0}", name); } Listing 2-18 is the equivalent query using the query expression syntax: Rattz_789-3C02.fm Page 37 Tuesday, October 16, 2007 2:19 PM 38 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ Listing 2-18. The Equivalent Query Using the Query Expression Syntax string[] names = { "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland", "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield", "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson", "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley", "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft", "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"}; IEnumerable<string> sequence = from n in names where n.Length < 6 select n; foreach (string name in sequence) { Console.WriteLine("{0}", name); } The first thing you may notice about the query expression example is that unlike SQL, the from statement precedes the select statement. One of the compelling reasons for this change is to narrow the scope for IntelliSense. Without this inversion of the statements, if in the Visual Studio 2008 text editor you typed select followed by a space, IntelliSense will have no idea what variables to display in its drop-down list. The scope of possible variables at this point is not restricted in any way. By specifying where the data is coming from first, IntelliSense has the scope of what variables to offer you for selection. Both of these examples provide the same results: Adams Bush Ford Grant Hayes Nixon Polk Taft Tyler It is important to note that the query expression syntax only translates the most common query operators: Where, Select, SelectMany, Join, GroupJoin, GroupBy, OrderBy, ThenBy, OrderByDescending, and ThenByDescending. Query Expression Grammar Your query expressions must adhere to the following rules: 1. A query expression must begin with a from clause. 2. The remainder of the query expression may then contain zero or more from, let, or where clauses. A from clause is a generator that declares one or more enumerator variables enu- merating over a sequence or a join of multiple sequences. A let clause introduces a variable and assigns a value to it. A where clause filters elements from the sequence or join of multiple sequences into the output sequence. Rattz_789-3C02.fm Page 38 Tuesday, October 16, 2007 2:19 PM CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ 39 3. The remainder of the query expression may then be followed by an orderby clause which contains one or more ordering fields with optional ordering direction. Direction is either ascending or descending. 4. The remainder of the query expression must then be followed by a select or group clause. 5. The remainder of the query expression may then be followed by an optional continuation clause. A continuation clause is either the into clause, zero or more join clauses, or another repeating sequence of these numbered elements beginning with the clauses in No. 2. An into clause directs the query results into an imaginary output sequence, which functions as a from clause for a subsequent query expression beginning with the clauses in No. 2. For a more technical yet less wordy description of the query expression syntax, use the following grammar diagram provided by Microsoft in the MSDN LINQ documentation: query-expression: from-clause query-body from-clause: from type opt identifier in expression join-clauses opt join-clauses: join-clause join-clauses join-clause join-clause: join type opt identifier in expression on expression equals expression join type opt identifier in expression on expression equals expression into identifier query-body: from-let-where-clauses opt orderby-clause opt select-or-group-clause query-continuation opt from-let-where-clauses: from-let-where-clause from-let-where-clauses from-let-where-clause from-let-where-clause: from-clause let-clause where-clause let-clause: let identifier = expression where-clause: where boolean-expression orderby-clause: orderby orderings Rattz_789-3C02.fm Page 39 Tuesday, October 16, 2007 2:19 PM 40 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ orderings: ordering orderings , ordering ordering: expression ordering-direction opt ordering-direction: ascending descending select-or-group-clause: select-clause group-clause select-clause: select expression group-clause: group expression by expression query-continuation: into identifier join-clauses opt query-body Query Expression Translation Now assuming you have created a syntactically correct query expression, the next issue becomes how the compiler translates the query expression into C# code. It must translate your query expression into the standard C# dot notation that I discuss in the query expression section. But how does it do this? To translate a query expression, the compiler is looking for code patterns in the query expression that need to be translated. The compiler will perform several translation steps in a specific order to translate the query expression into standard C# dot notation. Each translation step is looking for one or more related code patterns. The compiler must repeatedly translate all occurrences of the code patterns for that translation step in the query expression before moving on to the next translation step. Likewise, each step operates on the assumption that the query has had the code patterns for all previous translation steps translated. Transparent Identifiers Some translations insert enumeration variables with transparent identifiers. In the translation step descriptions in the next section, a transparent identifier is identified with an asterisk (*). This should not be confused with the SQL selected field wildcard character, *. When translating query expressions, sometimes additional enumerations are generated by the compiler, and transparent identifiers are used to enumerate through them. The transparent identifiers only exist during the translation process and once the query expression is fully translated no transparent identifiers will remain in the query. Rattz_789-3C02.fm Page 40 Tuesday, October 16, 2007 2:19 PM CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ 41 Translation Steps Next I discuss the translation steps. In doing so, I use the variable letters shown in Table 2-1 to represent specific portions of the query. Allow me to provide a word of warning. The soon to be described translation steps are quite complicated. Do not allow this to discourage you. You no more need to fully understand the transla- tion steps to write LINQ queries than you need to know how the compiler translates the foreach statement to use it. They are here to provide additional translation information should you need it, which should be rarely, or never. The translation steps are documented as code pattern ➤ translation. Oddly, even though I present the translation steps in the order the compiler performs them, I think the translation process is simpler to understand if you learn them in the reverse order. The reason is that when you look at the first translation step, it handles only the first code pattern translation and you are left with a lot of untranslated code patterns that you have yet to be introduced to. In my mind, this leaves a lot of unaccounted for gobbledygook. Since each translation step requires the previous translation step’s code patterns to already be translated, by the time you get to the final translation step, there is no gobbledygook left. I think this makes the final translation step easier to understand than the first. And in my opinion, traversing backward through the translation steps is the easiest way to under- stand what is going on. That said, here are the translation steps presented in the order in which the compiler performs them. Table 2-1. Translation Step Variables Variable Description Example c A compiler-generated temporary variable N/A e An enumerator variable from e in customers f Selected field element or new anonymous type from e in customers select f g A grouped element from e in s group g by k i An imaginary into sequence from e in s into i k Grouped or joined key element from e in s group g by k l A variable introduced by let from e in s let l = v o An ordering element from e in s orderby o s Input sequence from e in s v A value assigned to a let variable from e in s let l = v w A where clause from e in s where w Rattz_789-3C02.fm Page 41 Tuesday, October 16, 2007 2:19 PM 42 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ Select and Group Clauses with into Continuation Clause If your query expression contains an into continuation clause, the following translation is made: Here is an example: Explicit Enumeration Variable Types If your query expression contains a from clause that explicitly specifies an enumeration variable type, the following translation will be made: Here is an example: If your query expression contains a join clause that explicitly specifies an enumeration variable type, the following translation will be made: Rattz_789-3C02.fm Page 42 Tuesday, October 16, 2007 2:19 PM [...]... supplement your queries This portion of LINQ is known as LINQ to Objects Rattz_789-3.book Page 51 Tuesday, October 16, 20 07 2: 21 PM PART 2 ■■■ LINQ to Objects Rattz_789-3.book Page 52 Tuesday, October 16, 20 07 2: 21 PM Rattz_789-3.book Page 53 Tuesday, October 16, 20 07 2: 21 PM CHAPTER 3 ■■■ LINQ to Objects Introduction Listing 3-1 A Simple LINQ to Objects Query string[] presidents = { "Adams", "Arthur",... Demonstrating the Query Results Changing Between Enumerations // Create an array of ints int[] intArray = new int[] { 1 ,2, 3 }; IEnumerable ints = intArray.Select(i => i); // Display the results foreach(int i in ints) Console.WriteLine(i); // Change an element in the source data intArray[0] = 5; Console.WriteLine(" -"); Rattz_789-3.book Page 57 Tuesday, October 16, 20 07 2: 21 PM C HA PTER 3 ■ LINQ. .. element in the original array of integers Then I call the foreach statement again This causes ints to perform the query again Since I changed the element in the original array, and the query is being performed again because ints is being enumerated again, the changed element is returned Technically speaking, the query I called returned an object that implemented IEnumerable However, in most LINQ discussions... event-handling mechanism Microsoft will leverage this in its LINQ to SQL entity class generation tools so that you can hook into the entity classes at key points in time Finally, query expressions provide that warm fuzzy feeling when first seeing a LINQ query that makes you want to get on board with LINQ Nothing makes a developer analyzing a new technology feel comfortable quicker than technology resembling... // Display the results foreach(int i in ints) Console.WriteLine(i); 57 Rattz_789-3.book Page 58 Tuesday, October 16, 20 07 2: 21 PM 58 CHAPTER 3 ■ LIN Q TO OBJECTS IN TRODUCTION // Change an element in the source data intArray[0] = 5; Console.WriteLine(" -"); // Display the results again foreach(int i in ints) Console.WriteLine(i); Here are the results: 1 2 3 1 2 3 Notice the results do not change... the data source changes Listing 3-5 is the same as the previous code example except instead of having the query return an IEnumerable, it will return a List by calling the ToList operator Listing 3-5 Returning a List So the Query Is Executed Immediately and the Results Are Cached // Create an array of ints int[] intArray = new int[] { 1, 2, 3 }; List ints = intArray.Select(i => i).ToList();... output sequence In this way, the query is deferred In case you are unaware, when I use the term yield, I am referring to the C# 2. 0 yield keyword that was added to the C# language to make writing enumerators easier For example, examine the code in Listing 3 -2 Listing 3 -2 A Trivial Sample Query string[] presidents = { "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland", "Clinton", "Coolidge",... of ints int[] ints = new int[] { 1 ,2, 3,4,5,6 }; // Declare our delegate Func GreaterThanTwo = i => i > 2; // Perform the query not really Don't forget about deferred queries!!! IEnumerable intsGreaterThanTwo = ints.Where(GreaterThanTwo); // Display the results foreach(int i in intsGreaterThanTwo) Console.WriteLine(i); This code provides the following results: 3 4 5 6 The Standard Query. .. and proven technology By giving LINQ queries the ability to resemble SQL queries, Microsoft has made LINQ compelling to learn While all of these language enhancements by themselves are nice features, together they form the foundation for LINQ I believe that LINQ will be the next SQL or object-oriented bandwagon, and most NET developers will want LINQ on their résumé I know it’s going to be on mine... Operators, add a using System .Linq; directive to your code, if one is not already present You do not need to add an assembly reference because the code is contained in the System.Core.dll assembly, which is automatically added to your project by Visual Studio 20 08 Rattz_789-3.book Page 55 Tuesday, October 16, 20 07 2: 21 PM C HA PTER 3 ■ LINQ TO OBJEC TS INTRODUC TION Returning IEnumerable, Yielding, and Deferred . name); } Listing 2- 18 is the equivalent query using the query expression syntax: Rattz_789-3C 02. fm Page 37 Tuesday, October 16, 20 07 2: 19 PM 38 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ Listing 2- 18 orderings Rattz_789-3C 02. fm Page 39 Tuesday, October 16, 20 07 2: 19 PM 40 CHAPTER 2 ■ C# 3.0 LANGUAGE ENHANCEMENTS FOR LINQ orderings: ordering orderings , ordering ordering: expression ordering-direction opt ordering-direction: . into the entity classes at key points in time. Finally, query expressions provide that warm fuzzy feeling when first seeing a LINQ query that makes you want to get on board with LINQ. Nothing

Ngày đăng: 06/08/2014, 08:22

Từ khóa liên quan

Mục lục

  • C# 3.0 Language Enhancements for LINQ

    • New C# 3.0 Language Additions

      • Extension Methods

        • Extension Method Declarations and Invocations

        • Extension Method Precedence

        • Partial Methods

          • A Partial Method Example

          • What Is the Point of Partial Methods?

          • The Rules

          • Query Expressions

            • Query Expression Grammar

            • Query Expression Translation

            • Summary

            • LINQ to Objects Introduction

              • LINQ to Objects Overview

              • IEnumerable<T>, Sequences, and the Standard Query Operators

              • Returning IEnumerable<T>, Yielding, and Deferred Queries

              • Func Delegates

              • The Standard Query Operators Alphabetical Cross-Reference

              • Summary

              • Deferred Operators

                • Referenced Namespaces

                • Referenced Assemblies

                • Common Classes

                • The Deferred Operators by Purpose

                  • Restriction

                    • Where

                    • Projection

                      • Select

                      • SelectMany

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan