Siêu thị PDFTải ngay đi em, trời tối mất

Thư viện tri thức trực tuyến

Kho tài liệu với 50,000+ tài liệu học thuật

© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

Apress pro LINQ Language Integrated Query in C# 2008 phần 2 ppsx
PREMIUM
Số trang
75
Kích thước
2.1 MB
Định dạng
PDF
Lượt xem
1780

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

Nội dung xem thử

Mô tả chi tiết

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 typeopt identifier in expression join-clausesopt

join-clauses:

join-clause

join-clauses join-clause

join-clause:

join typeopt identifier in expression on expression equals

expression

join typeopt identifier in expression on expression equals

expression into identifier

query-body:

from-let-where-clausesopt orderby-clauseopt select-or-group-clause

query-continuationopt

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

Tải ngay đi em, còn do dự, trời tối mất!