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 3 pdf
PREMIUM
Số trang
52
Kích thước
873.9 KB
Định dạng
PDF
Lượt xem
898

Apress pro LINQ Language Integrated Query in C# 2008 phần 3 pdf

Nội dung xem thử

Mô tả chi tiết

108 CHAPTER 4 ■ DEFERRED OPERATORS

In the preceding code, notice I am enumerating through an outer sequence named outerSequence,

where each element is an object implementing IGrouping containing the key, and a sequence of

EmployeeOptionEntry elements having that same key.

Here are the results:

Option records for employee: 1

id=1 : optionsCount=2 : dateAwarded=12/31/1999

Option records for employee: 2

id=2 : optionsCount=10000 : dateAwarded=6/30/1992

id=2 : optionsCount=10000 : dateAwarded=1/1/1994

id=2 : optionsCount=10000 : dateAwarded=4/1/2003

Option records for employee: 3

id=3 : optionsCount=5000 : dateAwarded=9/30/1997

id=3 : optionsCount=7500 : dateAwarded=9/30/1998

id=3 : optionsCount=7500 : dateAwarded=9/30/1998

Option records for employee: 4

id=4 : optionsCount=1500 : dateAwarded=12/31/1997

Option records for employee: 101

id=101 : optionsCount=2 : dateAwarded=12/31/1998

For an example of the second GroupBy prototype, let’s assume I know that any employee whose

id is less than 100 is considered a founder of the company. Those with an id of 100 or greater are not

considered founders. My task is to list all option records grouped by the option record’s employee

founder status. All founders’ option records will be grouped together, and all nonfounders’ option

records will be grouped together.

Now, I need an equality comparer that can handle this key comparison for me. My equality

comparer must implement the IEqualityComparer interface. Before examining my comparer, let’s

take a look at the interface.

The iIEqualityComparer<T> Interface

interface IEqualityComparer<T> {

bool Equals(T x, T y);

int GetHashCode(T x);

}

This interface requires me to implement two methods, Equals and GetHashCode. The Equals

method is passed two objects of the same type T and returns true if the two objects are considered

to be equal or false otherwise. The GetHashCode method is passed a single object and returns a hash

code of type int for that object.

A hash code is a numerical value, typically mathematically calculated based on some portion

of the data in an object, known as the key, for the purpose of uniquely identifying the object. That

calculated hash code functions as the index into some data structure to store that object and find it

at a later time. Since it is typical for multiple keys to produce the same hash code, thereby making the

hash code truly less than unique, it is also necessary to be able to determine if two keys are equal.

This is the purpose of the Equals method.

Here is my class implementing the IEqualityComparer interface.

Rattz_789-3.book Page 108 Tuesday, October 16, 2007 2:21 PM

CHAPTER 4 ■ DEFERRED OPERATORS 109

A Class Implementing the IEqualityComparer Interface for My Second GroupBy Example

public class MyFounderNumberComparer : IEqualityComparer<int>

{

public bool Equals(int x, int y)

{

return(isFounder(x) == isFounder(y));

}

public int GetHashCode(int i)

{

int f = 1;

int nf = 100;

return (isFounder(i) ? f.GetHashCode() : nf.GetHashCode());

}

public bool isFounder(int id)

{

return(id < 100);

}

}

In addition to the methods required by the interface, I have added a method, isFounder, to

determine if an employee is a founder based on our definition. This just makes the code a little easier

to understand. I have made that method public so that I can call it from outside the interface, which

you will see me do in my example.

My equality comparer is going to consider any integer less than 100 as representing a founder,

and if two integers signify either both founders or both nonfounders, they are considered equal. For

the purposes of producing a hash code, I return a hash code of 1 for a founder and 100 for a nonfounder

so that all founders end up in the same group, and all nonfounders end up in another group.

My GroupBy example code is in Listing 4-31.

Listing 4-31. An Example of the Second GroupBy Prototype

MyFounderNumberComparer comp = new MyFounderNumberComparer();

EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

IEnumerable<IGrouping<int, EmployeeOptionEntry>> opts = empOptions

.GroupBy(o => o.id, comp);

// First enumerate through the sequence of IGroupings.

foreach (IGrouping<int, EmployeeOptionEntry> keyGroup in opts)

{

Console.WriteLine("Option records for: " +

(comp.isFounder(keyGroup.Key) ? "founder" : "non-founder"));

// Now enumerate through the grouping's sequence of EmployeeOptionEntry elements.

foreach (EmployeeOptionEntry element in keyGroup)

Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",

element.id, element.optionsCount, element.dateAwarded);

}

Rattz_789-3.book Page 109 Tuesday, October 16, 2007 2:21 PM

110 CHAPTER 4 ■ DEFERRED OPERATORS

In the example, I instantiate my equality comparer object ahead of time, as opposed to doing it

in the call to the GroupBy method, so that I can use it to call the isFounder method in the foreach loop.

Here are the results from this code:

Option records for: founder

id=1 : optionsCount=2 : dateAwarded=12/31/1999

id=2 : optionsCount=10000 : dateAwarded=6/30/1992

id=2 : optionsCount=10000 : dateAwarded=1/1/1994

id=3 : optionsCount=5000 : dateAwarded=9/30/1997

id=2 : optionsCount=10000 : dateAwarded=4/1/2003

id=3 : optionsCount=7500 : dateAwarded=9/30/1998

id=3 : optionsCount=7500 : dateAwarded=9/30/1998

id=4 : optionsCount=1500 : dateAwarded=12/31/1997

Option records for: non-founder

id=101 : optionsCount=2 : dateAwarded=12/31/1998

As you can see, all employee options records for an employee whose id is less than 100 are

grouped with the founders. Otherwise, they are grouped with the nonfounders.

For an example of the third GroupBy prototype, we’ll assume we are only interested in getting the

dates that the options were awarded for each employee. This code will be very similar to the example

for the first prototype.

So in Listing 4-32, instead of returning a sequence of groupings of EmployeeOptionEntry objects,

I will have groupings of dates.

Listing 4-32. An Example of the Third GroupBy Prototype

EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

IEnumerable<IGrouping<int, DateTime>> opts = empOptions

.GroupBy(o => o.id, e => e.dateAwarded);

// First enumerate through the sequence of IGroupings.

foreach (IGrouping<int, DateTime> keyGroup in opts)

{

Console.WriteLine("Option records for employee: " + keyGroup.Key);

// Now enumerate through the grouping's sequence of DateTime elements.

foreach (DateTime date in keyGroup)

Console.WriteLine(date.ToShortDateString());

}

Notice that in the call to the GroupBy operator, elementSelector, the second argument, is just

returning the dateAwarded member. Because I am returning a DateTime, my IGrouping is now for a

type of DateTime, instead of EmployeeOptionEntry.

Just as you would expect, I now have the award dates of the options grouped by employee:

Option records for employee: 1

12/31/1999

Option records for employee: 2

6/30/1992

1/1/1994

4/1/2003

Option records for employee: 3

9/30/1997

Rattz_789-3.book Page 110 Tuesday, October 16, 2007 2:21 PM

CHAPTER 4 ■ DEFERRED OPERATORS 111

9/30/1998

9/30/1998

Option records for employee: 4

12/31/1997

Option records for employee: 101

12/31/1998

For the fourth and final prototype, I need to use an elementSelector method and a comparer

object, so I will use a combination of the examples for prototypes two and three. I want to group the

dates of awarded options by whether they were awarded to a founding employee or not, where a

founding employee is one whose id is less than 100. That code is in Listing 4-33.

Listing 4-33. An Example of the Fourth GroupBy Prototype

MyFounderNumberComparer comp = new MyFounderNumberComparer();

EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

IEnumerable<IGrouping<int, DateTime>> opts = empOptions

.GroupBy(o => o.id, o => o.dateAwarded, comp);

// First enumerate through the sequence of IGroupings.

foreach (IGrouping<int, DateTime> keyGroup in opts)

{

Console.WriteLine("Option records for: " +

(comp.isFounder(keyGroup.Key) ? "founder" : "non-founder"));

// Now enumerate through the grouping's sequence of EmployeeOptionEntry elements.

foreach (DateTime date in keyGroup)

Console.WriteLine(date.ToShortDateString());

}

In the output, we should see just dates grouped by founders and nonfounders:

Option records for: founder

12/31/1999

6/30/1992

1/1/1994

9/30/1997

4/1/2003

9/30/1998

9/30/1998

12/31/1997

Option records for: non-founder

12/31/1998

Set

The set operators are used to perform mathematical set-type operations on sequences.

■Tip The prototypes of the set operators that are covered in this chapter do not work properly for DataSets. For

use with DataSets please use the prototypes that are covered in Chapter 10.

Rattz_789-3.book Page 111 Tuesday, October 16, 2007 2:21 PM

112 CHAPTER 4 ■ DEFERRED OPERATORS

Distinct

The Distinct operator removes duplicate elements from an input sequence.

Prototypes

The Distinct operator has one prototype I will cover.

The Distinct Prototype

public static IEnumerable<T> Distinct<T>(

this IEnumerable<T> source);

This operator returns an object that, when enumerated, enumerates the elements of the input

sequence named source and yields any element that is not equal to a previously yielded element. An

element is determined to be equal to another element using their GetHashCode and Equals methods.

Isn’t it fortuitous that I just covered how and why the GetHashCode and Equals methods are used?

Exceptions

ArgumentNullException is thrown if the source argument is null.

Examples

For this example, I am going to first display the count of the presidents array, next I will concatenate

the presidents array with itself, display the count of the resulting concatenated sequence, then call

the Distinct operator on that concatenated sequence, and finally display the count of the distinct

sequence which should be the same as the initial presidents array.

To determine the count of the two generated sequences, I will use the Count Standard Query

Operator. Since it is a nondeferred operator, I will not cover it in this chapter. I will cover it in the next

chapter, though. For now, just be aware that it returns the count of the sequence on which it is called.

The code is in Listing 4-34.

Listing 4-34. An Example of the Distinct Operator

string[] presidents = {

"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"};

// Display the count of the presidents array.

Console.WriteLine("presidents count: " + presidents.Count());

// Concatenate presidents with itself. Now each element should

// be in the sequence twice.

IEnumerable<string> presidentsWithDupes = presidents.Concat(presidents);

// Display the count of the concatenated sequence.

Console.WriteLine("presidentsWithDupes count: " + presidentsWithDupes.Count());

// Eliminate the duplicates and display the count.

IEnumerable<string> presidentsDistinct = presidentsWithDupes.Distinct();

Console.WriteLine("presidentsDistinct count: " + presidentsDistinct.Count());

Rattz_789-3.book Page 112 Tuesday, October 16, 2007 2:21 PM

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