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
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