从delegate到lambda表达式

先看一个简单的delegate的例子:

代码
 1  public static bool IsOdd(int i)
 2         {
 3             return (i & 1== 1;
 4         }
 5         public delegate bool NumberTester(int i);
 6         public static void PrintMatchingNumbers(int from, int to, NumberTester filter)
 7         {
 8             for (int i = from; i <= to; ++i)
 9             {
10                 if (filter(i))
11                 {
12                     Console.WriteLine(i);
13                 }
14             }
15         }

 

调用时只需要执行:

1 PrintMatchingNumbers(110new NumberTester(IsOdd));

 

很多情况下,我们只要执行只包含一个方法的代理。于是微软在.net 2.0里给我们提供了更方便的anonymous method,比如上面调用IsOdd的过程就能改写成下面这样:

1 PrintMatchingNumbers(110delegate(int i)
2         {
3             return (i & 1== 1;
4         });

 

如此就避免了IsOdd的声明。微软提供匿名方法的道理是这样的:既然函数IsOdd只在一个地方被调用,那么直接在调用的地方实现岂不更省事儿。这样可以减少我们开发人员的输入。

接着到了3.0,微软更进了一步,他提出了lambda表达式。C#lambda表达式是这样定义的参数=> 表达式。于是,我们的代码变的更短:

1 PrintMatchingNumbers(110, i=>(i & 1== 1);

 

也就是说lambda表达式作为一个函数并当作PrintMatchingNumbers的第三个参数来执行,而这个函数只要符合delegate NumberTester的签名就行。理所当然的lambda表达式可以附值给一个相同签名的delegate:

1  NumberTester a =new NumberTester();
2                 a += i=>(i & 1== 1;

 

C# 3.0里甚至可以把声明和初始化以及赋值合到一块儿,简写成: 

1 NumberTester a = i=>(i & 1== 1;

 

如果我们把NumberTester声明为泛型的delegate,如下:

1 public delegate U NumberTester<T, U>(T i);

 

那么这个泛型的delegate应用就太广泛了,涵盖了一切有一个输入参数和一个返回值的函数,所以直接命名为Func更合适,即:

1  public delegate U Func<T, U>(T i);

 

其实上面这个类型正是在.net Framework 3.0 中最重要的命名空间 System.Core里定义的。这样,就可以写成 

1 Func<int,bool> a = i=>(i & 1== 1;

 

下面我们再回过头来看看使用这个delegatePrintMatchingNumbers,应该作相应的更改:

代码
 1 public static void PrintMatchingNumbers(int from, int to, Func<intbool> filter)
 2   {
 3    for (int i = from; i <= to; ++i)
 4    {
 5       if (filter(i))
 6       {
 7          Console.WriteLine(i);
 8       }
 9    }
10   }

 

当然,由于泛型的的引入,此处还可以进一步的把类型抽离出来以表达这个意思:在一个范围内的任意的类型的变量,以指定的方式递增,通过判断打印符合条件的值。也就可以得到算法的复用。完整的例子:

代码
 1 public delegate U Func<T, U>(T i);
 2 public delegate void Func<T>(ref T i);
 3 static void Main(string[] args)
 4 {
 5   DateTime from = DateTime.Parse("2004/06/01");
 6   DateTime to = DateTime.Parse("2005/01/18");
 7   PrintMatchingT<DateTime>(from, to,
 8   delegate(ref DateTime i) { i = i.AddDays(1); },
 9   delegate(DateTime i) 
10           { return (i.Month + i.Day == 11);});
11             Console.WriteLine();
12             Console.Read();
13           }
14   public static void PrintMatchingT<T>(T from, T to, Func<T> incre, Func<T, bool> filter) where T : IComparable<T>
15         {
16           for (T i = from; (i.CompareTo(to) < 0); incre(ref i))
17           {
18             if (filter(i))
19             {
20               Console.WriteLine(i);
21             }
22           }
23        }

 

这个例子是在指定的时间范围内以天为递增量,判断代表月与日的数字加起来为11的日子。当然具体到这个例子,我们也能把bool改成泛型的,只要保证实际传入的类型能够有效的转化成bool类型的,不然filter(i)是没法放到 if判断中的。

 另外,如何创建自己的Linq provider (实现IQueryable)(翻译)

http://www.cnblogs.com/sweatypalms/archive/2007/10/18/929312.html

本文来自于博客园李小刀:http://www.cnblogs.com/sweatypalms/archive/2007/11/22/967958.html 

 

由此可见:混合使用泛型和匿名代理是可以写出很精简的代码,当然这也往往意味着需要写更多的注释来说明你的意图。 

posted on 2010-04-27 14:40  woodcutter  阅读(709)  评论(0编辑  收藏  举报

导航