Conversion Operators


The methods included in the conversion operator set are AsEnumerable, ToArray, ToList, ToDictionary, ToLookup, OfType, and Cast. Conversion operators are mainly defined to solve the problems and the needs that we illustrated in the previous two sections. Sometimes you might need a stable and immutable result from a query expression, or you might want to use a generic extension method operator instead of a more specialized one. In the following sections, we will describe the conversion operators in more detail.

AsEnumerable

The signature for AsEnumerable is shown here:

 public static IEnumerable<T> AsEnumerable<T>(     this IEnumerable<T> source);

The AsEnumerable operator simply returns the source sequence as an object of type IEnumerable<T>. This kind of “conversion on the fly” makes it possible to call the general-purpose extension methods over source, even if its type has specific implementations of them. You can see an example in Listing 4-57.

Listing 4-57: A query expression over a list of Customers converted with the AsEnumerable operator

image from book
  Customers customersList = new Customers(customers); var expr =     from   c in customersList.AsEnumerable()     where  c.City == "Brescia"     select c; foreach (var item in expr) {     Console.WriteLine(item); } 
image from book

The code in Listing 4-57 will use the standard Where operator defined for IEnumerable<T> within System.Linq.Enumerable.

ToArray and ToList

Two other useful conversion operators are ToArray and ToList. They convert a source sequence of type IEnumerable<T> into an array of T (T[]) or into a generic list of T (List<T>), respectively:

 public static T[] ToArray<T>(     this IEnumerable<T> source); public static List<T> ToList<T>(     this IEnumerable<T> source);

The results of these operators are snapshots of the sequence. When they are applied inside a query expression, the result will be stable and unchanged, even if the source sequence does change. Listing 4-58 shows an example of using ToList.

Listing 4-58: A query expression over an immutable list of Customers obtained by the ToList operator

image from book
  List<Customer> customersList = new List<Customer>(customers); var expr =     from   c in customersList.ToList()     where  c.Country == Countries.Italy     select c; foreach (var item in expr) {     Console.WriteLine(item); } 
image from book

These methods are also useful whenever you need to enumerate the result of a query many times, executing the query only once for performance reasons. Consider the sample in Listing 4-59. It would probably be inefficient to refresh the list of products to join with orders every time. Therefore, you can create a “copy” of the products query.

Listing 4-59: A query expression that uses ToList to copy the result of a query over products

image from book
  var productsQuery =     (from p in products      where p.Price >= 30      select p)     .ToList(); var ordersWithProducts =     from c in customers         from   o in c.Orders         join   p in productsQuery                on o.IdProduct equals p.IdProduct         select new { p.IdProduct, o.Quantity, p.Price,                      TotalAmount = o.Quantity * p.Price}; foreach (var order in ordersWithProducts) {     Console.WriteLine(order); } 
image from book

Every time you enumerate the ordersWithProducts expression-for instance, in a foreach block-the productsQuery expression will not be evaluated again.

ToDictionary

Another operator in this set is the ToDictionary extension method. It creates an instance of Dictionary<K, T>. The keySelector predicate identifies the key of each item. The elementSelector, if provided, is used to extract each single item. These predicates are defined through the available signatures:

 public static Dictionary<K, T> ToDictionary<T, K>(     this IEnumerable<T> source,     Func<T, K> keySelector); public static Dictionary<K, T> ToDictionary<T, K>(     this IEnumerable<T> source,     Func<T, K> keySelector,     IEqualityComparer<K> comparer); public static Dictionary<K, E> ToDictionary<T, K, E>(     this IEnumerable<T> source,     Func<T, K> keySelector,     Func<T, E> elementSelector); public static Dictionary<K, E> ToDictionary<T, K, E>(     this IEnumerable<T> source,     Func<T, K> keySelector,     Func<T, E> elementSelector,     IEqualityComparer<K> comparer);

When the method constructs the resulting dictionary, it assumes the uniqueness of each key extracted by invoking the keySelector. In cases of duplicate keys, an ArgumentException error will be thrown. The key values are compared using the comparer argument if provided or EqualityComparer<K>.Default if not. In Listing 4-60, we use this operator to create a dictionary of customers.

Listing 4-60: An example of the ToDictionary operator, applied to customers

image from book
  var customersDictionary =     customers     .ToDictionary(c => c.Name,                   c => new {c.Name, c.City}); 
image from book

The first argument of the operator is the keySelector predicate, which extracts the customer Name as the key. The second argument is elementSelector, which creates an anonymous type that consists of customer Name and City properties. Here is the result of the query in Listing 4-60:

 [Paolo, {Name=Paolo, City=Brescia}] [Marco, {Name=Marco, City=Torino}] [James, {Name=James, City=Dallas}] [Frank, {Name=Frank, City=Seattle}]

Important 

Like the ToList and ToArray operators, ToDictionary copies the source sequence items rather than creating references to them. The ToDictionary method in Listing 4-60 effectively evaluates the query expression and creates the output dictionary. Therefore, customersDictionary does not have a deferred query evaluation behavior; it is the result produced by a statement execution.

ToLookup

Another conversion operator is ToLookup, which can be used to create enumerations of type Lookup<K, T>, whose definition follows:

 public class Lookup<K, T> : IEnumerable<IGrouping<K, T>> {     public int Count { get; }     public IEnumerable<T> this[K key] { get; }     public bool Contains(K key);     public IEnumerator<IGrouping<K, T>> GetEnumerator(); }

Each object of this type represents a one-to-many dictionary, which defines a tuple of keys and sequences of items, somewhat like the result of a GroupJoin method. Here are the available signatures:

 public static Lookup<K, T> ToLookup<T, K>(     this IEnumerable<T> source,     Func<T, K> keySelector); public static Lookup<K, T> ToLookup<T, K>(     this IEnumerable<T> source,     Func<T, K> keySelector,     IEqualityComparer<K> comparer); public static Lookup<K, E> ToLookup<T, K, E>(     this IEnumerable<T> source,     Func<T, K> keySelector,     Func<T, E> elementSelector); public static Lookup<K, E> ToLookup<T, K, E>(     this IEnumerable<T> source,     Func<T, K> keySelector,     Func<T, E> elementSelector,     IEqualityComparer<K> comparer);

As in ToDictionary, there is a keySelector predicate, an elementSelector predicate, and a comparer. The sample in Listing 4-61 demonstrates how to use this method to extract all orders for each product.

Listing 4-61: An example of the ToLookup operator, used to group orders by product

image from book
  var ordersByProduct =     (from c in customers          from   o in c.Orders          select o)     .ToLookup(o => o.IdProduct); Console.WriteLine( "\n\nNumber of orders for Product 1: {0}\n",                    ordersByProduct[1].Count()); foreach (var product in ordersByProduct) {     Console.WriteLine("Product: {0}", product.Key);     foreach(var order in product) {         Console.WriteLine("  {0}", order);     } } 
image from book

As you can see, Lookup<K, T> is accessible through an item key (ordersByProduct[1]) or through enumeration (the foreach loop). The following is the output of this example:

 Number of orders for Product 1: 2 Product: 1   3 - False - January – 1   10 - False - July – 1 Product: 2   5 - True - May – 2 Product: 3   20 - True - December – 3   20 - True - December – 3 Product: 5   20 - False - July - 5

OfType and Cast

The last two operators of this set are OfType and Cast. The first filters the source sequence, yielding only items of type T. It is useful in the case of sequences with items of different types. For instance, working with an object-oriented approach, you might have an object with a common base class and particular specialization in derived classes:

 public static IEnumerable<T> OfType<T>(     this IEnumerable source);

If you provide a type T that is not supported by any of the source items, the operator will return an empty sequence.

The Cast operator enumerates the source sequence and tries to yield each item, cast to type T. In the case of failure, an InvalidCastException error will be thrown.

 public static IEnumerable<T> Cast<T>(     this IEnumerable source);

Because of its signature, which accepts any IEnumerable sequence, this method can be used to convert old nongeneric types to newer IEnumerable<T> types. This conversion makes it possible to query these types with LINQ even if the types are unaware of LINQ.

Important 

Each item returned by OfType and Cast is a reference to the original object and not a copy. OfType does not create a snapshot of a source; instead, it evaluates the source every time you enumerate the operator’s result. This behavior is different from other conversion operators.




Introducing Microsoft LINQ
Introducing MicrosoftВ® LINQ
ISBN: 0735623910
EAN: 2147483647
Year: 2007
Pages: 78

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net