Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Daniel Solis - Illustrated C# 2010 - 2010.pdf
Скачиваний:
16
Добавлен:
11.06.2015
Размер:
11.23 Mб
Скачать

CHAPTER 21 INTRODUCTION TO LINQ

Delegates As Parameters

As you just saw in the previous section, the first parameter of every operator is a reference to an IEnumerable<T> object. The parameters following it can be of any type. Many operators take generic delegates as parameters. (Generic delegates were explained in Chapter 19.) The most important thing to recall about generic delegates as parameters is the following:

Generic delegates are used to supply user-defined code to the operator.

To explain this, I’ll start with an example showing several ways you might use the Count operator. The Count operator is overloaded and has two forms. The first form, which was used in the previous example, has a single parameter, as shown here:

public static int Count<T>(this IEnumerable<T> source);

Like all extension methods, you can use it in the standard static method form or in the form of an instance method on an instance of the class it extends, as shown in the following two lines of code:

var

count1

=

Linq.Enumerable.Count(intArray);

//

Static method form

var

count2

=

intArray.Count();

//

Instance method form

In these two instances, the query counts the number of ints in the given integer array. Suppose, however, that you only want to count the odd elements of the array. To do that, you must supply the Count method with code that determines whether an integer is odd.

To do this, you would use the second form of the Count method, which is shown following. It has a generic delegate as its second parameter. At the point it is invoked, you must supply a delegate object that takes a single input parameter of type T and returns a Boolean value. The return value of the delegate code must specify whether the element should be included in the count.

public static int Count<T>(this IEnumerable<T> source,

Func<T, bool> predicate );

Generic delegate

569

CHAPTER 21 INTRODUCTION TO LINQ

For example, the following code uses the second form of the Count operator to instruct it to include only those values that are odd. It does this by supplying a lambda expression that returns true if the input value is odd and false otherwise. (Lambda expressions were covered in Chapter 15.) At each iteration through the collection, Count calls this method (represented by the lambda expression) with the current value as input. If the input is odd, the method returns true, and Count includes the element in the total.

static void Main()

{

int[] intArray = new int[] { 3, 4, 5, 6, 7, 9 };

var countOdd = intArray.Count(n => n % 2 == 1);

Lambda expression identifying the odd values

Console.WriteLine("Count of odd numbers: {0}", countOdd);

}

This code produces the following output:

Count of odd numbers: 4

570

CHAPTER 21 INTRODUCTION TO LINQ

The LINQ Predefined Delegate Types

Like the Count operator from the previous example, many of the LINQ operators require you to supply code that directs how the operator performs its operation. You can do this by using delegate objects as parameters.

Remember from Chapter 15 that you can think of a delegate object as an object that contains a method or list of methods with a particular signature and return type. When the delegate is invoked, the methods it contains are invoked in sequence.

LINQ defines two families of generic delegate types for use with the standard query operators. These are the Func delegates and the Action delegates. Each set has 17 members.

The delegate objects you create for use as actual parameters must be of these delegate types or of these forms.

TR represents the return type and is always last in the list of type parameters.

The first four generic Func delegates are listed here. The first form takes no method parameters and returns an object of the return type. The second takes a single method parameter and returns a value, and so forth. Notice that the return type parameter has the out keyword, making it covariant. It can therefore accept the type declared or any type derived from that type. The input parameters have the in keyword, making them contravariant. They, therefore, can accept the declared type, or any type derived from that type.

public delegate TR Func<out TR>

 

( );

 

 

 

public delegate TR Func<in T1, out TR

>

( T1

a1 );

 

 

public delegate TR Func<in T1, in T2,

out TR >

( T1

a1, T2

a2 );

public delegate TR Func<in T1, in T2,

in T3, out TR>( T1

a1, T2

a2, T3 a3 );

 

 

 

 

 

Return type

Type parameters

 

Method parameters

With this in mind, if you look again at the declaration of Count, which follows, you can see that the second parameter must be a delegate object that takes a single value of some type T as the method parameter and returns a value of type bool.

public static int Count<T>(this IEnumerable<T> source,

Func<T, bool> predicate );

↑ ↑

Parameter type Return type

A parameter delegate that produces a Boolean value is called a predicate.

The first four Action delegates are the following. They’re the same as the Func delegates except that they have no return value and hence no return value type parameter. All their type parameters are contravariant.

public delegate void Action

( );

 

 

 

public delegate void Action<in T1>

( T1

a1 );

 

public

delegate

void

Action<in T1, in T2>

(

T1

a1,

T2

a2 );

public

delegate

void

Action<in T1, in T2, in T3>(

T1

a1,

T2

a2, T3 a3 );

571

CHAPTER 21 INTRODUCTION TO LINQ

Example Using a Delegate Parameter

Now that you better understand Count’s signature and LINQ’s use of generic delegate parameters, you’ll be better able to understand a full example.

The following code first declares method IsOdd, which takes a single parameter of type int and returns a bool value stating whether the input parameter was odd. Method Main does the following:

It declares an array of ints as the data source.

It creates a delegate object called MyDel of type Func<int, bool>, and it uses method IsOdd to initialize the delegate object. Notice that you don’t need to declare the Func delegate type because, as you saw, it’s already predefined by LINQ.

It calls Count using the delegate object.

class Program

{

static bool IsOdd(int x) // Method to be used by the delegate object

{

return x % 2 == 1; // Return true if x is odd.

}

static

void Main()

 

 

{

 

 

 

int[] intArray

= new int[] { 3, 4, 5,

6, 7, 9 };

Func<int, bool> myDel = new Func<int,

bool>(IsOdd); // Delegate object

var

countOdd =

intArray.Count(myDel);

// Use delegate

Console.WriteLine("Count of odd numbers: {0}", countOdd);

}

}

This code produces the following output:

Count of odd numbers: 4

572

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]