本文转载自http://www.cnblogs.com/niyw/archive/2010/10/07/1845232.html

 

简介

  在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆。我想下面的代码能证实这点。下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个First不仅被编译,并都获得正确答案,且他们的结果一样。如果你对此感到困惑,那么请继续看这篇文章。

 

代码
 1 class Customer 
 2 
 3     public int ID { getset; } 
 4     public static bool Test(Customer x) 
 5     { 
 6         return x.ID == 5
 7     } 
 8 
 9 ... 
10 List<Customer> custs = new List<Customer>(); 
11 custs.Add(new Customer() { ID = 1 }); 
12 custs.Add(new Customer() { ID = 5 }); 
13  
14 custs.First(new Func<Customer, bool>(delegate(Customer x) { return x.ID == 5; })); 
15 custs.First(new Func<Customer, bool>((Customer x) => x.ID == 5)); 
16 custs.First(delegate(Customer x) { return x.ID == 5; }); 
17 custs.First((Customer x) => x.ID == 5); 
18 custs.First(x => x.ID == 5); 
19 custs.First(Customer.Test); 

 

 

什么是委托?

  现在你定义一个处理用户订单的购物车ShoppingCart类。管理层决定根据数量,价格等给客人折扣。做为其中的一部分,他们已经实现了处理订单时你要考虑一方面。不用考虑过多,你简单声明一个变量来保存有“吸引力的折扣”(magicDisCount),然后实现逻辑。

 

代码
 1 class Program { 
 2     static void Main(string[] args)  { 
 3         new ShoppingCart().Process(); 
 4     } 
 5 }  
 6 class ShoppingCart { 
 7     public void Process() { 
 8         int magicDiscount = 5
 9         // ... 
10     }
11 }

 

 

第二天,异想天开的管理层决定根据购买时间调整折扣。这个很简单,但需要你改动一点代码。

 

1 class ShoppingCart { 
2     public void Process() { 
3         int magicDiscount = 5
4         if (DateTime.Now.Hour < 12) { 
5             magicDiscount = 10
6         } 
7     } 
8 }

 

 

  接下来一段时间里,管理层又反复添加更多的折扣逻辑。这时你就会在心理抱怨“受够了”。那么我该怎么做才能把这些无聊的逻辑从我的代码中剥离出去,让该处理的人去处理呢?这时你要做的是移交或者委派给相应职能的别人。幸运的是,.NET为此提供了一种叫做“委托”的机制。

委托

  如果你有C/C++编程背景,描述委托最好的方法是“函数指针”。对所有人来说,可以认为把委托传递给方法与把值或对象传递给方法一样。比如下面三行代码就表现出一样的基本原则:你在传递数据给Process处理而不是你自己使用。

 

1 // 给方法Process传递一个整形值
2 Process( 5 ); 
3 // 给方法Process传递一个ArrayList的引用
4 Process( new ArrayList() ); 
5 // 给方法Process传递一个方法的引用
6 Process( discountDelegate );

 

 

DiscountDelegate是什么?我如何创建?Process方法如何使用?首先如同声明一个类一样,声明一个委托类型。

 

1 delegate int DiscountDelegate(); 

 

 

这句话的意思是我们有一个叫DiscountDelegate的委托类型,我们可以像使用类,结构体等一样使用它。它不需要数据参数,但返回一个整数值。像类一样,我们必须创建一个它的实例它才有意义。记住,创建一个委托实例实质上是创建一个方法的引用。创建实例时关键是要明白DiscountDelegate没有任何构造器,它有一个隐式的构造函数来构造一个与它相同签名的方法(没有传入参数,返回一个整数)。那你怎么给这个构造函数一个方法呢?.NET向你提供了一个向它名字一样简单的方法,你所做的只是忽略圆括号。
1 DiscountDelegate discount = new DiscountDelegate(class.method); 

 

在深入之前,先回到开始的例子,整理一个代码。我们会添加一个Calculator类来帮助我们处理折扣逻辑,并给我们的委托提供一些方法。
 1 delegate int DiscountDelegate(); 
 2  
 3 class Program { 
 4     static void Main(string[] args) { 
 5         Calculator calc = new Calculator(); 
 6         DiscountDelegate discount = null
 7         if (DateTime.Now.Hour < 12) { 
 8             discount = new DiscountDelegate(calc.Morning); 
 9         } 
10         else if (DateTime.Now.Hour < 20) { 
11             discount = new DiscountDelegate(calc.Afternoon); 
12         } 
13         else { 
14             discount = new DiscountDelegate(calc.Night); 
15         } 
16         new ShoppingCart().Process(discount); 
17     } 
18 }  
19 class Calculator { 
20     public int Morning() { 
21         return 5
22     } 
23     public int Afternoon() { 
24         return 10
25     } 
26     public int Night() { 
27         return 15
28     } 
29 }  
30 class ShoppingCart { 
31     public void Process(DiscountDelegate discount) { 
32         int magicDiscount = discount(); 
33         // ... 
34     } 
35 }

正如你所见,在Calculator类中,我们为每个逻辑分支创建了一个方法。在Main方法中,我们创建一个Calculator实例和一个DiscountDelegate实例,并按照我们所期望的把它们整合在一起。

  太棒了,我们不用担心Process方法中的逻辑了,我们只需要简单得回调我们定义的委托。记住!我们不关心委托是如何创建的(或什么时间),我们就像调用其他方法一样调用它。如你所见,

另一种理解委托的方法是,它延迟执行一个方法。Calculator方法在过去某个时间本选择,但不会执行,直到我们调用discount()的时候。现在看看我们的解决方案,

这里仍然存在一些丑陋的代码。在Calculator类中,我们可以用一个不同的方法来返回替代每个有返回值得方法吗?答案是肯定的,让我们把这些乱糟糟的代码合并起来。

 

 1 delegate int DiscountDelegate(); 
 2  
 3 class Program { 
 4     static void Main(string[] args) { 
 5         new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate)); 
 6     } 
 7 }  
 8 class Calculator { 
 9     public static int Calculate() { 
10         int discount = 0
11         if (DateTime.Now.Hour < 12) { 
12             discount = 5
13         } 
14         else if (DateTime.Now.Hour < 20) { 
15             discount = 10
16         } 
17         else { 
18             discount = 15
19         } 
20         return discount; 
21     } 
22 }  
23 class ShoppingCart { 
24     public void Process(DiscountDelegate discount) { 
25         int magicDiscount = discount(); 
26          // ... 
27     } 
28 }

 

  这样子看起来更好点。你会注意到我们用一个静态的Calculate方法替换了所有原来的方法,在Main方法中也不用费心维护一个指向DiscountDelegate的引用。

现在你明白了所有关于委托的东西了吗?在2004年.NET1.1中可以这么说,但是很不幸的是,这种框架自那以后更加成熟了。

灯光,镜头,开始 或者我们需要Func!

  微软在.NET 2.0中引入了泛型,并提供了一个泛型委托:Action<T>。老实说,我认为它远不够用。后来在.NET 3.5中,

它为我们提供了一些我们不想定义的通用委托。他们扩展了Action,并添加了Func,二者唯一区别在于Func型方法有一个返回值而Action型方法没有。

  这意味着我们不需要声明自己的DiscountDelegate,可以用Func<int>替代。为说明这些观点是如何工作的,我们来假设管理层又一次改变了

我们的逻辑,我们需要提供一些特殊的折扣。很简单,我们将给Calculate方法传入一个bool型值。

  现在我们的委托签名变成Func<bool,int>。注意Calculate方法现在包含一个bool型参数,我们用一个bool值调用discount()。

 

 1 class Program { 
 2     static void Main(string[] args) { 
 3         new ShoppingCart().Process(new Func<boolint>(Calculator.Calculate)); 
 4     } 
 5 
 6  
 7 class Calculator { 
 8     public static int Calculate(bool special) { 
 9         int discount = 0
10         if (DateTime.Now.Hour < 12) { 
11             discount = 5
12         } 
13         else if (DateTime.Now.Hour < 20) { 
14             discount = 10
15         } 
16         else if (special) { 
17             discount = 20
18         } 
19         else { 
20             discount = 15
21         } 
22         return discount; 
23     } 
24 
25  
26 class ShoppingCart { 
27     public void Process(Func<bool,int> discount) { 
28         int magicDiscount = discount(false); 
29         int magicDiscount2 = discount(true); 
30     } 
31 }

相关文章: