Tải bản đầy đủ (.pdf) (98 trang)

Addison Wesley Essential C Sharp_6 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.86 MB, 98 trang )

ptg
Anonymous Types and Implicitly Typed Local Variables 541
Listing 14.2: Type Safety and Immutability of Anonymous Types
class Program
{
static void Main()
{
var patent1 =
new
{
};
var patent2 =
new
{
};
var patent3 =
new
{
patent1.Title,
};
// ERROR: Cannot implicitly convert type
// 'AnonymousType#1' to 'AnonymousType#2'
patent1 = patent2;
// ERROR: Cannot implicitly convert type
// 'AnonymousType#3' to 'AnonymousType#2'
patent1 = patent3;

// ERROR: Property or indexer 'AnonymousType#1.Title'
// cannot be assigned to it is read only'
patent1.Title = "Swiss Cheese";
}


}
The resultant two compile errors assert the fact that the types are not com-
patible, so they will not successfully convert from one to the other.
The third compile error is caused by the reassignment of the Title
property. Anonymous types are immutable, so it is a compile error to
change a property on an anonymous type once it has been instantiated.
Although not shown in Listing 14.2, it is not possible to declare a
method with an implicit data type parameter (var). Therefore, instances
Title = "Bifocals",
YearOfPublication = "1784"
YearOfPublication = "1877",
Title = "Phonograph"
Year = patent1.YearOfPublication
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators542
of anonymous types can only be passed outside the method in which they
are created in only two ways. First, if the method parameter is of type
object, the anonymous type instance may pass outside the method
because the anonymous type will convert implicitly. A second way is to
use method type inference, whereby the anonymous type instance is
passed as a method type parameter that the compiler can successfully
infer. Calling void Method<T>(T parameter) using Function(patent1),
therefore, would succeed, although the available operations on parameter
within Function() are limited to those supported by object.
In spite of the fact that C# allows anonymous types such as the ones
shown in Listing 14.1, it is generally not recommended that you define
them in this way. Anonymous types provide critical functionality with C#
3.0 support for projections, such as joining/associating collections, as we
discuss later in the chapter. However, generally you should reserve anony-

mous type definitions for circumstances where they are required, such as
aggregation of data from multiple types.
ADVANCED TOPIC
Anonymous Type Generation
Even though Console.WriteLine()’s implementation is to call ToString(),
notice in Listing 14.1 that the output from Console.WriteLine() is not the
default ToString(), which writes out the fully qualified data type name.
Rather, the output is a list of PropertyName = value pairs, one for each
property on the anonymous type. This occurs because the compiler over-
rides ToString() in the anonymous type code generation, and instead for-
mats the ToString() output as shown. Similarly, the generated type
includes overriding implementations for Equals() and GetHashCode().
The implementation of ToString() on its own is an important reason
that variance in the order of properties causes a new data type to be gener-
ated. If two separate anonymous types, possibly in entirely separate types
and even namespaces, were unified and then the order of properties
changed, changes in the order of properties on one implementation would
have noticeable and possibly unacceptable effects on the others’ ToString()
From the Library of Wow! eBook
ptg
Collection Initializers 543
results. Furthermore, at execution time it is possible to reflect back on a type
and examine the members on a type—even to call one of these members
dynamically (determining at runtime which member to call). A variance in
the order of members on two seemingly identical types could trigger unex-
pected results, and to avoid this, the C# designers decided to generate two
different types.
Collection Initializers
Another feature added to C# in version 3.0 was collection initializers. A
collection initializer allows programmers to construct a collection with an

initial set of members at instantiation time in a manner similar to array
declaration. Without collection initialization, elements had to be explicitly
added to a collection after the collection was instantiated—using some-
thing like System.Collections.Generic.ICollection<T>’s Add() method.
With collection initialization, the Add() calls are generated by the C# com-
plier rather than explicitly coded by the developer. Listing 14.3 shows how
to initialize the collection using a collection initializer instead.
Listing 14.3: Filtering with System.Linq.Enumerable.Where()
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> sevenWorldBlunders;
sevenWorldBlunders = new List<string>()
{
// Quotes from Ghandi
"Wealth without work",
"Pleasure without conscience",
"Knowledge without character",
"Commerce without morality",
"Science without humanity",
"Worship without sacrifice",
"Politics without principle"
};
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators544
Print(sevenWorldBlunders);

}
private static void Print<T>(IEnumerable<T> items)
{
foreach (T item in items)
{
Console.WriteLine(item);
}
}
}
The syntax is similar not only to the array initialization, but also to an
object initializer with the curly braces following the constructor. If no
parameters are passed in the constructor, the parentheses following the
data type are optional (as they are with object initializers).
A few basic requirements are needed in order for a collection initializer
to compile successfully. Ideally, the collection type to which a collection ini-
tializer is applied would be of a type that implements
System.Collec-
tions.Generic.ICollection<T>. This ensures that the collection includes
an Add() that the compiler-generated code can invoke. However, a relaxed
version of the requirement also exists and simply demands that one or more
Add() methods exist on a type that implements IEnumerable<T>—even if
the collection doesn’t implement ICollection<T>. The Add() methods need
to take parameters that are compatible with the values specified in the col-
lection initializer.
Allowing initializers on collections that don’t support ICollection<T>
was important for two reasons. First, it turns out that the majority of collec-
tions (types that implement IEnumerable<T>) do not also implement
ICollection<T>, thus significantly reducing the usefulness of collection
initializers.
Second, matching on the method name and signature compatibility

with the collection initialize items enables greater diversity in the items ini-
tialized into the collection. For example, the initializer now can support
new DataStore(){ a, {b, c}} as long as there is one Add() method whose
signature is compatible with a and a second Add() method compatible
with b, c.
From the Library of Wow! eBook
ptg
Collection Initializers 545
Note that you cannot have a collection initializer for an anonymous type
since the collection initializer requires a constructor call, and it is impossible
to name the constructor. The workaround is to define a method such as
static List<T> CreateList<T>(T t) { return new List<T>(); }. Method
type inference allows the type parameter to be implied rather than specified
explicitly, and so this workaround successfully allows for the creation of a
collection of anonymous types.
Another approach to initializing a collection of anonymous types is to
use an array initializer. Since it is not possible to specify the data type in
the constructor, array initialization syntax allows for anonymous array ini-
tializers using
new[] (see Listing 14.4).
Listing 14.4: Initializing Anonymous Type Arrays
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
new
{

TeamName = "France",
Players = new string[]
{
"Fabien Barthez", "Gregory Coupet",
"Mickael Landreau", "Eric Abidal",
//
}
},
new
{
TeamName = "Italy",
Players = new string[]
{
"Gianluigi Buffon", "Angelo Peruzzi",
"Marco Amelia", "Cristian Zaccardo",
//
}
}
};
var worldCup2006Finalists = new[]
{
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators546
Print(worldCup2006Finalists);
}
private static void Print<T>(IEnumerable<T> items)
{
foreach (T item in items)
{

Console.WriteLine(item);
}
}
}
The resultant variable is an array of the anonymous type items, which
must be homogenous since it is an array.
What Makes a Class a Collection: IEnumerable<T>
By definition, a collection within .NET is a class that, at a minimum, imple-
ments IEnumerable<T> (technically, it would be the nongeneric type IEnu-
merable). This interface is a key because implementing the methods of
IEnumerable<T> is the minimum implementation requirement needed to
support iterating over the collection.
Chapter 3 showed how to use a foreach statement to iterate over an
array of elements. The syntax is simple and avoids the complication of
having to know how many elements there are. The runtime does not
directly support the foreach statement, however. Instead, the C# compiler
transforms the code as described in this section.
foreach with Arrays
Listing 14.5 demonstrates a simple foreach loop iterating over an array of
integers and then printing out each integer to the console.
Listing 14.5: foreach with Arrays
int[] array = new int[]{1, 2, 3, 4, 5, 6};
foreach (int item in array)
{
Console.WriteLine(item);
}
From the Library of Wow! eBook
ptg
What Makes a Class a Collection: IEnumerable<T> 547
From this code, the C# compiler creates a CIL equivalent of the for

loop, as shown in Listing 14.6.
Listing 14.6: Compiled Implementation of foreach with Arrays
int number;
int[] tempArray;
int[] array = new int[]{1, 2, 3, 4, 5, 6};
tempArray = array;
for (int counter = 0; (counter < tempArray.Length); counter++)
{
int item = tempArray[counter];
Console.WriteLine(item);
}
In this example, note that foreach relies on support for the Length
property and the index operator ([]). With the Length property, the C#
compiler can use the for statement to iterate through each element in the
array.
foreach with IEnumerable<T>
Although the code shown in Listing 14.6 works well on arrays where the
length is fixed and the index operator is always supported, not all types of
collections have a known number of elements. Furthermore, many of the col-
lection classes, including the Stack<T>, Queue<T>, and Dictionary<Tkey,
Tvalue> classes, do not support retrieving elements by index. Therefore, a
more general approach of iterating over collections of elements is needed.
The iterator pattern provides this capability. Assuming you can determine
the first, next, and last elements, knowing the count and supporting retrieval
of elements by index is unnecessary.
The System.Collections.Generic.IEnumerator<T> and nongeneric
System.Collections.IEnumerator interfaces (see Listing 14.8) are designed
to enable the iterator pattern for iterating over collections of elements, rather
than the length-index pattern shown in Listing 14.6. A class diagram of their
relationships appears in Figure 14.1.

From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators548
IEnumerator, which IEnumerator<T> derives from, includes three
members. The first is bool MoveNext(). Using this method, you can move
from one element within the collection to the next while at the same time
detecting when you have enumerated through every item. The second
member, a read-only property called Current, returns the element cur-
rently in process. Current is overloaded in IEnumerator<T>, providing a
type-specific implementation of it. With these two members on the collec-
tion class, it is possible to iterate over the collection simply using a while
loop, as demonstrated in Listing 14.7. (The Reset() method usually throws
a NotImplementedException and, therefore, should never be called. If you
need to restart an enumeration, just create a fresh enumerator.)
Listing 14.7: Iterating over a Collection Using while
System.Collections.Generic.Stack<int> stack =
new System.Collections.Generic.Stack<int>();
int number;
//
// This code is conceptual, not the actual code.
while (stack.MoveNext())
{
number = stack.Current;
Console.WriteLine(number);
}
Figure 14.1: IEnumerator<T> and IEnumerator Interfaces
IDisposable
IEnumerator
Interface
Interface

IEnumerable
Interface
IEnumerableϽTϾ
Generic Interface
IEnumeratorϽTϾ
Generic Interface
Methods
Methods
Methods
IEnumerable
Methods
Properties
Properties
IDisposable
IEnumerator
Dispose
Current
Current
MoveNext
Reset
GetEnumerator
GetEnumerator
From the Library of Wow! eBook
ptg
What Makes a Class a Collection: IEnumerable<T> 549
In Listing 14.7, the MoveNext() method returns false when it moves past the
end of the collection. This replaces the need to count elements while looping.
Listing 14.7 uses a System.Collections.Generic.Stack<T> as the col-
lection type. Numerous other collection types exist; this is just one exam-
ple. The key trait of Stack<T> is its design as a last in, first out (LIFO)

collection. It is important to note that the type parameter T identifies the
type of all items within the collection. Collecting one particular type of
object within a collection is a key characteristic of a generic collection. It
is important that the programmer understands the data type within
the collection when adding, removing, or accessing items within the
collection.
This preceding example shows the gist of the C# compiler output, but it
doesn’t actually compile that way because it omits two important details
concerning the implementation: interleaving and error handling.
State Is Shared
The problem with an implementation such as Listing 14.7 is that if two
such loops interleaved each other—one foreach inside another, both
using the same collection—the collection must maintain a state indicator
of the current element so that when MoveNext() is called, the next ele-
ment can be determined. The problem is that one interleaving loop can
affect the other. (The same is true of loops executed by multiple threads.)
To overcome this problem, the collection classes do not support IEnu-
merator<T> and IEnumerator interfaces directly. As shown in Figure 14.1,
there is a second interface, called IEnumerable<T>, whose only method is
GetEnumerator(). The purpose of this method is to return an object that
supports IEnumerator<T>. Instead of the collection class maintaining the
state, a different class, usually a nested class so that it has access to the
internals of the collection, will support the IEnumerator<T> interface and
will keep the state of the iteration loop. The enumerator is like a “cursor”
or a “bookmark” in the sequence. You can have multiple bookmarks, and
moving each of them enumerates over the collection independently of the
other. Using this pattern, the C# equivalent of a foreach loop will look like
the code shown in Listing 14.8.
From the Library of Wow! eBook
ptg

Chapter 14: Collection Interfaces with Standard Query Operators550
Listing 14.8: A Separate Enumerator Maintaining State during an Iteration
System.Collections.Generic.Stack<int> stack =
new System.Collections.Generic.Stack<int>();
int number;
System.Collections.Generic.Stack<int>.Enumerator
enumerator;
//
// If IEnumerable<T> is implemented explicitly,
// then a cast is required.
// ((IEnumerable<int>)stack).GetEnumerator();
enumerator = stack.GetEnumerator();
while (enumerator.MoveNext())
{
number = enumerator.Current;
Console.WriteLine(number);
}
Cleaning Up Following Iteration
Since the classes that implement the IEnumerator<T> interface maintain
the state, sometimes you need to clean up the state after it exits the loop
(because either all iterations have completed or an exception is thrown). To
achieve this, the IEnumerator<T> interface derives from IDisposable. Enu-
merators that implement IEnumerator do not necessarily implement IDis-
posable, but if they do, Dispose() will be called as well. This enables the
calling of Dispose() after the foreach loop exits. The C# equivalent of the
final CIL code, therefore, looks like Listing 14.9.
Listing 14.9: Compiled Result of foreach on Collections
System.Collections.Generic.Stack<int> stack =
new System.Collections.Generic.Stack<int>();
System.Collections.Generic.Stack<int>.Enumerator

enumerator;
IDisposable disposable;
enumerator = stack.GetEnumerator();
try
{
int number;
while (enumerator.MoveNext())
{
number = enumerator.Current;
Console.WriteLine(number);
}
}
From the Library of Wow! eBook
ptg
What Makes a Class a Collection: IEnumerable<T> 551
finally
{
// Explicit cast used for IEnumerator<T>.
disposable = (IDisposable) enumerator;
disposable.Dispose();
// IEnumerator will use the as operator unless IDisposable
// support is known at compile time.
// disposable = (enumerator as IDisposable);
// if (disposable != null)
// {
// disposable.Dispose();
// }
}
Notice that because the IDisposable interface is supported by IEnu-
merator<T>, the using statement can simplify the code in Listing 14.9 to

that shown in Listing 14.10.
Listing 14.10: Error Handling and Resource Cleanup with using
System.Collections.Generic.Stack<int> stack =
new System.Collections.Generic.Stack<int>();
int number;
{
while (enumerator.MoveNext())
{
number = enumerator.Current;
Console.WriteLine(number);
}
}
However, recall that the CIL also does not directly support the using key-
word, so in reality, the code in Listing 14.9 is a more accurate C# represen-
tation of the foreach CIL code.
ADVANCED TOPIC
foreach without IEnumerable
Technically, the compiler doesn’t require that IEnumerable/IEnumera-
ble<T> be supported in order to iterate over a data type using foreach.
using(
System.Collections.Generic.Stack<int>.Enumerator<int>
enumerator = stack.GetEnumerator())
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators552
Rather, the compiler uses a concept known as “duck typing” such that if no
IEnumerable/IEnumerable<T> method is found, it looks for the GetEnu-
merator() method to return a type with Current() and MoveNext() meth-
ods. Duck typing involves searching for a method by name rather than
relying on an interface or explicit method call to the method.

Do Not Modify Collections during foreach Iteration
Chapter 3 showed that the compiler prevents assignment of the foreach
variable (number). As is demonstrated in Listing 14.10, an assignment to
number would not be a change to the collection element itself, so the C#
compiler prevents such an assignment altogether.
In addition, neither the element count within a collection nor the items
themselves can generally be modified during the execution of a foreach
loop. If, for example, you called stack.Push(42) inside the foreach loop, it
would be ambiguous whether the iterator should ignore or incorporate
the change to stack—in other words, whether iterator should iterate
over the newly added item or ignore it and assume the same state as when
it was instantiated.
Because of this ambiguity, an exception of type System.InvalidOpera-
tionException is generally thrown upon accessing the enumerator if the
collection is modified within a foreach loop, reporting that the collection
was modified after the enumerator was instantiated.
Standard Query Operators
Besides the methods on System.Object, any type that implements IEnu-
merable<T> has only one method, GetEnumerator(). And yet, it makes
more than 50 methods available to all types implementing IEnumera-
ble<T>, not including any overloading—and this happens without need-
ing to explicitly implement any method except the GetEnumerator()
method. The additional functionality is provided using C# 3.0’s extension
methods and it all resides in the class System.Linq.Enumerable. Therefore,
including the using declarative for System.Linq is all it takes to make these
methods available.
Each method on IEnumerable<T> is a standard query operator; it pro-
vides querying capability over the collection on which it operates. In the
From the Library of Wow! eBook
ptg

Standard Query Operators 553
following sections, we will examine some of the most prominent of these
standard query operators.
Many of the examples will depend on an Inventor and/or Patent class,
both of which are defined in Listing 14.11.
Listing 14.11: Sample Classes for Use with Standard Query Operators
using System;
using System.Collections.Generic;
using System.Linq;
public class Patent
{
// Title of the published application
public string Title { get; set; }
// The date the application was officially published
public string YearOfPublication { get; set; }
// A unique number assigned to published applications
public string ApplicationNumber { get; set; }
public long[] InventorIds { get; set; }
public override string ToString()
{
return string.Format("{0}({1})",
Title, YearOfPublication);
}
}
public class Inventor
{
public long Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }

public string Country { get; set; }
public override string ToString()
{
return string.Format("{0}({1}, {2})",
Name, City, State);
}
}
class Program
{
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators554
static void Main()
{
IEnumerable<Patent> patents = PatentData.Patents;
Print(patents);
Console.WriteLine();
IEnumerable<Inventor> inventors = PatentData.Inventors;
Print(inventors);
}
private static void Print<T>(IEnumerable<T> items)
{
foreach (T item in items)
{
Console.WriteLine(item);
}
}
}
public static class PatentData
{

public static readonly Inventor[] Inventors = new Inventor[]
{
new Inventor(){
Name="Benjamin Franklin", City="Philadelphia",
State="PA", Country="USA", Id=1 },
new Inventor(){
Name="Orville Wright", City="Kitty Hawk",
State="NC", Country="USA", Id=2},
new Inventor(){
Name="Wilbur Wright", City="Kitty Hawk",
State="NC", Country="USA", Id=3},
new Inventor(){
Name="Samuel Morse", City="New York",
State="NY", Country="USA", Id=4},
new Inventor(){
Name="George Stephenson", City="Wylam",
State="Northumberland", Country="UK", Id=5},
new Inventor(){
Name="John Michaelis", City="Chicago",
State="IL", Country="USA", Id=6},
new Inventor(){
Name="Mary Phelps Jacob", City="New York",
State="NY", Country="USA", Id=7},
};
public static readonly Patent[] Patents = new Patent[]
{
From the Library of Wow! eBook
ptg
Standard Query Operators 555
new Patent(){

Title="Bifocals", YearOfPublication="1784",
InventorIds=new long[] {1}},
new Patent(){
Title="Phonograph", YearOfPublication="1877",
InventorIds=new long[] {1}},
new Patent(){
Title="Kinetoscope", YearOfPublication="1888",
InventorIds=new long[] {1}},
new Patent(){
Title="Electrical Telegraph",
YearOfPublication="1837",
InventorIds=new long[] {4}},
new Patent(){
Title="Flying machine", YearOfPublication="1903",
InventorIds=new long[] {2,3}},
new Patent(){
Title="Steam Locomotive",
YearOfPublication="1815",
InventorIds=new long[] {5}},
new Patent(){
Title="Droplet deposition apparatus",
YearOfPublication="1989",
InventorIds=new long[] {6}},
new Patent(){
Title="Backless Brassiere",
YearOfPublication="1914",
InventorIds=new long[] {7}},
};
}
Listing 14.11 also provides a selection of sample data. Output 14.2 displays

the results.
OUTPUT 14.2:
Bifocals(1784)
Phonograph(1877)
Kinetoscope(1888)
Electrical Telegraph(1837)
Flying machine(1903)
Steam Locomotive(1815)
Droplet deposition apparatus(1989)
Backless Brassiere(1914)
Benjamin Franklin(Philadelphia, PA)
Orville Wright(Kitty Hawk, NC)
Wilbur Wright(Kitty Hawk, NC)
Samuel Morse(New York, NY)
George Stephenson(Wylam, Northumberland)
John Michaelis(Chicago, IL)
Mary Phelps Jacob(New York, NY)
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators556
Filtering with Where()
In order to filter out data from a collection, we need to provide a filter
method that returns true or false, indicating whether a particular
element should be included or not. A delegate expression that takes an
argument and returns a Boolean is called a predicate, and a collection’s
Where() method depends on predicates for identifying filter criteria, as
shown in Listing 14.12. (Technically, the result of the Where() method is a
monad which encapsulates the operation of filtering a given sequence
with a given predicate.) The output appears in Output 14.3.
Listing 14.12: Filtering with System.Linq.Enumerable.Where()

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
IEnumerable<Patent> patents = PatentData.Patents;
Print(patents);
}
//
}
Notice that the code assigns the output of the Where() call back to
IEnumerable<T>. In other words, the output of IEnumerable<T>.Where()
is a new IEnumerable<T> collection. In Listing 14.12, it is IEnumera-
ble<Patent>.
patents = patents.Where(
patent => patent.YearOfPublication.StartsWith("18"));
OUTPUT 14.3:
Phonograph(1877)
Kinetoscope(1888)
Electrical Telegraph(1837)
Steam Locomotive(1815)
From the Library of Wow! eBook
ptg
Standard Query Operators 557
Less obvious is that the Where() expression argument has not necessar-
ily executed at assignment time. This is true for many of the standard
query operators. In the case of Where(), for example, the expression is
passed in to the collection and “saved” but not executed. Instead, execu-

tion of the expression occurs only when it is necessary to begin iterating
over the items within the collection. A foreach loop, for example, such as
the one in Print() (in Listing 14.11), will trigger the expression to be
evaluated for each item within the collection. At least conceptually, the
Where() method should be understood as a means of specifying the query
regarding what appears in the collection, not the actual work involved
with iterating over to produce a new collection with potentially fewer
items.
Projecting with Select()
Since the output from the IEnumerable<T>.Where() method is a new
IEnumerable<T> collection, it is possible to again call a standard query
operator on the same collection. For example, rather than just filtering
the data from the original collection, we could transform the data (see
Listing 14.13).
Listing 14.13: Projection with System.Linq.Enumerable.Select()
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
IEnumerable<Patent> patents = PatentData.Patents;
IEnumerable<Patent> patentsOf1800 = patents.Where(
patent => patent.YearOfPublication.StartsWith("18"));

Print(items);
}
//
}

IEnumerable<string> items = patentsOf1800.Select(
patent => patent.ToString());
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators558
In Listing 14.13, we create a new IEnumerable<string> collection. In
this case, it just so happens that adding the Select() call doesn’t change
the output; but this is only because Print()’s Console.WriteLine() call
used ToString() anyway. Obviously, a transform still occurred on each
item from the Patent type of the original collection to the string type of the
items collection.
Consider the example using System.IO.FileInfo in Listing 14.14.
Listing 14.14: Projection with System.Linq.Enumerable.Select() and new
//
IEnumerable<string> fileList = Directory.GetFiles(
rootDirectory, searchPattern);
IEnumerable<FileInfo> files = fileList.Select(
file => new FileInfo(file));
//
fileList is of type IEnumerable<string>. However, using the projection
offered by Select, we can transform each item in the collection to a
System.IO.FileInfo object.
Lastly, capitalizing on anonymous types, we could create an IEnumera-
ble<T> collection where T is an anonymous type (see Listing 14.15 and
Output 14.4).
Listing 14.15: Projection to an Anonymous Type
//
IEnumerable<string> fileList = Directory.GetFiles(
rootDirectory, searchPattern);
//

var items = fileList.Select(
file =>
{
FileInfo fileInfo = new FileInfo(file);
return new
{
FileName = fileInfo.Name,
Size = fileInfo.Length
};
});
From the Library of Wow! eBook
ptg
Standard Query Operators 559
The output of an anonymous type automatically shows the property
names and their values as part of the generated ToString() method associ-
ated with the anonymous type.
Projection using the Select() method is very powerful. We already
saw how to filter a collection vertically (reducing the number of items in
the collection) using the Where() standard query operator. Now, via the
Select() standard query operator, we can also reduce the collection
horizontally (making fewer columns) or transform the data entirely. In
combination, Where() and Select() provide a means for extracting only
the pieces of the original collection that are desirable for the current
algorithm. These two methods alone provide a powerful collection
manipulation API that would otherwise result in significantly more code
that is less readable.
ADVANCED TOPIC
Running LINQ Queries in Parallel
With the abundance of computers having multiple processors and multi-
ple cores within those processors, the ability to easily take advantage of the

additional processing power becomes far more important. To do this, pro-
grams need to be changed to support multiple threads so that work can
happen simultaneously on different CPUs within the computer. Listing
14.16 demonstrates one way to do this using Parallel LINQ (PLINQ).
OUTPUT 14.4:
{ FileName = AssemblyInfo.cs, Size = 1704 }
{ FileName = CodeAnalysisRules.xml, Size = 735 }
{ FileName = CustomDictionary.xml, Size = 199 }
{ FileName = EssentialCSharp.sln, Size = 40415 }
{ FileName = EssentialCSharp.suo, Size = 454656 }
{ FileName = EssentialCSharp.vsmdi, Size = 499 }
{ FileName = EssentialCSharp.vssscc, Size = 256 }
{ FileName = intelliTechture.ConsoleTester.dll, Size = 24576 }
{ FileName = intelliTechture.ConsoleTester.pdb, Size = 30208 }
{ FileName = LocalTestRun.testrunconfig, Size = 1388 }
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators560
Listing 14.16: Executing LINQ Queries in Parallel
//
IEnumerable<string> fileList = Directory.GetFiles(
rootDirectory, searchPattern);
file =>
{
FileInfo fileInfo = new FileInfo(file);
return new
{
FileName = fileInfo.Name,
Size = fileInfo.Length
};

});
//
As Listing 14.16 shows, the change in code to enable parallel support is
minimal. All that it uses is a .NET Framework 4 introduced standard
query operator, AsParallel(), on the static class System.Linq.Paral-
lelEnumerable. Using this simple extension method, however, the run-
time begins executing over the items within the fileList collection and
returning the resultant objects in parallel. Each parallel operation in this
case isn’t particularly expensive (although it is relative to what other exe-
cution is taking place), but consider CPU-intensive operations such as
encryption or compression. Paralyzing the execution across multiple
CPUs can decrease execution time by a magnitude corresponding to the
number of CPUs.
An important caveat to be aware of (and the reason why AsParallel()
appears in an Advanced Block rather than the standard text) is that parallel
execution can introduce race conditions such that an operation on one
thread can be intermingled with an operation on a different thread, caus-
ing data corruption. To avoid this, synchronization mechanisms are
required on data with shared access from multiple threads in order to force
the operations to be atomic where necessary. Synchronization itself, how-
ever, can introduce deadlocks that freeze the execution, further complicat-
ing the effective parallel programming.
More details on this and additional multithreading topics are covered
in Chapter 18 and Chapter 19.
var items = fileList. AsParallel()
.Select(
From the Library of Wow! eBook
ptg
Standard Query Operators 561
Counting Elements with Count()

Another common query performed on a collection of items is to retrieve
the count. To support this LINQ includes the Count() extension method.
Listing 14.17 demonstrates that Count() is overloaded to simply count
all elements (no parameters) or to take a predicate that only counts items
identified by the predicate expression.
Listing 14.17: Counting Items with Count()
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
IEnumerable<Patent> patents = PatentData.Patents;
}
//
}
In spite of the simplicity of writing the Count() statement, IEnumera-
ble<T> has not changed, so the executed code still involves iterating over
all the items in the collection. Whenever a Count property is directly avail-
able on the collection, it is preferable to use that rather than LINQ’s Count()
method (a subtle difference). Fortunately, ICollection<T> includes the
Count property, so code that calls the Count() method on a collection that
supports ICollection<T> will cast the collection and call Count directly.
However, if ICollection<T> is not supported, Enumerable.Count() will
proceed to enumerate all the items in the collection rather than call the
built-in Count mechanism. If the purpose of checking the count is only to
see whether it is greater than zero (if(patents.Count() > 0){ }), a
preferable approach would be to use the Any() operator (if(pat-
ents.Any()){ }). Any() attempts to iterate over only one of the items in

the collection to return a true result, rather than the entire sequence.
Console.WriteLine("Patent Count: {0}", patents. Count() );
Console.WriteLine("Patent Count in 1800s: {0}",
patents.Count(patent =>
patent.YearOfPublication.StartsWith("18")))
;
From the Library of Wow! eBook
ptg
Chapter 14: Collection Interfaces with Standard Query Operators562
Deferred Execution
One of the most important concepts to remember when using LINQ is
deferred execution. Consider the code in Listing 14.18 and the correspond-
ing output in Output 14.5.
Listing 14.18: Filtering with System.Linq.Enumerable.Where()
using System;
using System.Collections.Generic;
using System.Linq;
//
IEnumerable<Patent> patents = PatentData.Patents;
bool result;
patents = patents.Where(
patent =>
{
if (result =
patent.YearOfPublication.StartsWith("18"))
{
// Side effects like this in a predicate
// are used here to demonstrate a
// principle and should generally be
// avoided.

Console.WriteLine("\t" + patent);
}
return result;
});
Console.WriteLine("1. Patents prior to the 1900s are:");
foreach (Patent patent in patents)
{
}
Console.WriteLine();
Console.WriteLine(
"2. A second listing of patents prior to the 1900s:");
Console.WriteLine(
" There are {0} patents prior to 1900.",
patents.Count());
Console.WriteLine();
Console.WriteLine(
"3. A third listing of patents prior to the 1900s:");
patents = patents.ToArray();
From the Library of Wow! eBook
ptg
Standard Query Operators 563
Console.Write(" There are ");
Console.WriteLine("{0} patents prior to 1900.",
patents.Count());
//
Notice that Console.WriteLine("1. Patents prior…) executes before
the lambda expression. This is a very important characteristic to pay atten-
tion to because it is not obvious to those who are unaware of its impor-
tance. In general, predicates should do exactly one thing—evaluate a
condition—and they should not have any side effects (even printing to the

console, as in this example).
To understand what is happening, recall that lambda expressions are
delegates—references to methods—that can be passed around. In the con-
text of LINQ and standard query operators, each lambda expression forms
part of the overall query to be executed.
At the time of declaration, lambda expressions do not execute. It isn’t
until the lambda expressions are invoked that the code within them begins
to execute. Figure 14.2 shows the sequence of operations.
As Figure 14.2 shows, three calls in Listing 14.16 trigger the lambda
expression, and each time it is fairly implicit. If the lambda expression was
OUTPUT 14.5:
1. Patents prior to the 1900s are:
Phonograph(1877)
Kinetoscope(1888)
Electrical Telegraph(1837)
Steam Locomotive(1815)
2. A second listing of patents prior to the 1900s:
Phonograph(1877)
Kinetoscope(1888)
Electrical Telegraph(1837)
Steam Locomotive(1815)
There are 4 patents prior to 1900.
3. A third listing of patents prior to the 1900s:
Phonograph(1877)
Kinetoscope(1888)
Electrical Telegraph(1837)
Steam Locomotive(1815)
There are 4 patents prior to 1900.
From the Library of Wow! eBook
ptg

564
Figure 14.2: IEnumerator<T> and IEnumerator Interfaces
Program
Where<Patent>
WriteLine
GetEnumerator
Current{get}
MoveNext
WriteLine
Count<Patent>
WriteLine
WriteLine
ToArray<Patent>
Write
Count<Patent>
WriteLine
Main
List Display Triggered
List Display Triggered
List NOT Triggered
Enumerable Console IEnumerable<Patent> IEnumerable<Patent> IEnumerator
List Display Triggered for Item
1
2
3
From the Library of Wow! eBook
ptg
Standard Query Operators 565
expensive (such as a call to a database) it would be important to minimize
the lambda expression’s execution.

First, the execution is triggered within the
foreach loop. As I described
earlier in the chapter, the foreach loop breaks down into a MoveNext() call
and each call results in the lambda expression’s execution for each item in
the original collection. While iterating, the runtime invokes the lambda
expression for each item to determine whether the item satisfies the
predicate.
Second, a call to
Enumerable’s Count() (the function) triggers the
lambda expression for each item once more. Again, this is very subtle since
Count (the property) is very common on collections that have not been
queried with a standard query operator.
Third, the call to ToArray() (or ToList(), ToDictionary(), or ToLook-
up()) triggers the lambda expression for each item. However, converting
the collection with one of these “To” methods is extremely helpful. Doing
so returns a collection on which the standard query operator has already
executed. In Listing 14.16, the conversion to an array means that when
Length is called in the final Console.WriteLine(), the underlying object
pointed to by patents is in fact an array (which obviously implements
IEnumerable<T>), and therefore, System.Array’s implementation of
Length is called and not System.Linq.Enumerable’s implementation.
Therefore, following a conversion to one of the collection types returned
by a “To” method, it is generally safe to work with the collection (until
another standard query operator is called). However, be aware that this
will bring the entire result set into memory (it may have been backed by a
database or file before this). Furthermore, the “To” method will snapshot
the underlying data so that no fresh results will be returned upon requery-
ing the “To” method result.
I strongly encourage readers to review the sequence diagram in Figure
14.2 along with the corresponding code and understand the fact that the

deferred execution of standard query operators can result in extremely
subtle triggering of the standard query operators; therefore, developers
should use caution to avoid unexpected calls. The query object represents
the query, not the results. When you ask the query for the results, the
whole query executes (perhaps even again) because the query object
From the Library of Wow! eBook

×