技术开发 频道

一次性搞定C#委托、匿名和Lambda表达式

  正如你所见,在Calculator类中,我们为每个逻辑分支创建了一个方法。在Main方法中,我们创建一个Calculator实例和一个DiscountDelegate实例,并按照我们所期望的把它们整合在一起。  太棒了,我们不用担心Process方法中的逻辑了,我们只需要简单得回调我们定义的委托。记住!我们不关心委托是如何创建的(或什么时间),我们就像调用其他方法一样调用它。如你所见,另一种理解委托的方法是,它延迟执行一个方法。Calculator方法在过去某个时间本选择,但不会执行,直到我们调用discount()的时候。现在看看我们的解决方案,这里仍然存在一些丑陋的代码。在Calculator类中,我们可以用一个不同的方法来返回替代每个有返回值得方法吗?答案是肯定的,让我们把这些乱糟糟的代码合并起来。

delegate int DiscountDelegate();

class Program {
    static void Main(
string[] args) {
        
new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate));
    }
}  
class Calculator {
    
public static int Calculate() {
        
int discount = 0;
        
if (DateTime.Now.Hour < 12) {
            discount
= 5;
        }
        
else if (DateTime.Now.Hour < 20) {
            discount
= 10;
        }
        
else {
            discount
= 15;
        }
        return discount;
    }
}  
class ShoppingCart {
    
public void Process(DiscountDelegate discount) {
        
int magicDiscount = discount();
        
// ...
    }
}

 

  这样子看起来更好点。你会注意到我们用一个静态的Calculate方法替换了所有原来的方法,在Main方法中也不用费心维护一个指向DiscountDelegate的引用。现在你明白了所有关于委托的东西了吗?在2004年.NET1.1中可以这么说,但是很不幸的是,这种框架自那以后更加成熟了。

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

  微软在.NET 2.0中引入了泛型,并提供了一个泛型委托:Action。老实说,我认为它远不够用。后来在.NET 3.5中,它为我们提供了一些我们不想定义的通用委托。他们扩展了Action,并添加了Func,二者唯一区别在于Func型方法有一个返回值而Action型方法没有。

  这意味着我们不需要声明自己的DiscountDelegate,可以用Func替代。为说明这些观点是如何工作的,我们来假设管理层又一次改变了我们的逻辑,我们需要提供一些特殊的折扣。很简单,我们将给Calculate方法传入一个bool型值。

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

class Program {
    static void Main(
string[] args) {
        
new ShoppingCart().Process(new Func<bool, int>(Calculator.Calculate));
    }
}

class Calculator {
    
public static int Calculate(bool special) {
        
int discount = 0;
        
if (DateTime.Now.Hour < 12) {
            discount
= 5;
        }
        
else if (DateTime.Now.Hour < 20) {
            discount
= 10;
        }
        
else if (special) {
            discount
= 20;
        }
        
else {
            discount
= 15;
        }
        return discount;
    }
}

class ShoppingCart {
    
public void Process(Func<bool,int> discount) {
        
int magicDiscount = discount(false);
        
int magicDiscount2 = discount(true);
    }
}

 

  好像还算不错,我们又省了一行代码,这样算结束了吗?当然没有,我们甚至能省掉类型判断。只要我们传递的方法有严格签名的委托,.NET允许我们完全忽略掉显式创建Func。

//因为Process期望的方法有一个bool型输入参数和返回一个int值,所以下面这句话是正确的
new ShoppingCart().Process(Calculator.Calculate);

 

  至此,首先通过忽略自定义委托,我们省略了代码;然后排出了明确的创建Func委托。我们能继续压缩代码行吗?到此我们才完成此文的一半,答案显然是“能”。

  匿名方法

  匿名方法能够让你声明一个方法体而不需要给它指定一个名字。在接下来的场景里,它们以一个“普通的”方法存在;但是在你的代码中没有任何方法显式调用它。匿名方法只能在使用委托的时候创建,事实上,它们通过delegate关键字创建。

class Program {
    static void Main(
string[] args)     {
        
new ShoppingCart().Process(
            
new Func<bool, int>(delegate(bool x) { return x ? 10 : 5; }
        ));
    }
}

 

  正如你所见,我们完全删除了Calculator类的需求。你可以在打括号中添加任何其他方法中的逻辑。如果你在看它如何执行时有困难,那就把delegate(bool x)做为一个方法签名,而不是一个关键字。设想这段代码在一个类里,delegate(bool x){return 5;}是一个完整的合法方法声明(我们确实有一个返回值),恰好delegate是一个保留字,在这里,它让这个方法匿名。

  至此,我确信现在你知道这里我们甚至能压缩更多的代码。顺利成章的,我们能忽略显式声明Func委托的需要;.NET让我们使用delegate关键字更方便。

class Program {
    static void Main(
string[] args) {
        
new ShoppingCart().Process(
          delegate(bool x) { return x ?
10 : 5; }
        );
    }
}

 

  当把.NET方法做为委托参数时或处理时间时,就能看到匿名方法的真正用处。之前,你会为你所关注的所有可能行为创建了一个方法,现在你仅需以内联的方式创建它们,并可以避免污染你的命名空间。

// 创建一个匿名比对方法
custs.Sort(delegate(Customer c1, Customer c2) {
    return Comparer
<int>.Default.Compare(c1.ID, c2.ID);
});

//  创建一个匿名事件
button1.Click
+= delegate(object o, EventArgs e) { MessageBox.Show("Click!"); };

  

0
相关文章