技术开发 频道

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

  Lambda 表达式

  MSDN中写道:“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。你应当明白“用户创建委托”部分,但什么是“表达式”呢?老实说,表达式和表达式树不在此为讨论范围内。现在我们唯一需要明白的是,表达式是.NET程序运行时表示数据或对象的代码(C#代码)。引用Jon Skeet的话:“表达式树是一种表达逻辑,这样其他的代码可以查询的方法。当一个lambda表达式转换成一个表达式树,编译器不会发出了lambda表达式的白细胞介素,它会发出白细胞介素这将会建立一个表达式树表示相同的逻辑。”

  我们需要关注的是Lambda表达式替换匿名方法,和其他的特性。回顾我们最后例子,我们已经在一行代码里压缩了处理整个折扣算法的逻辑。

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

 

  你相信我们能让这个更短吗?Lambda表达式用'=>'运算符表明什么参数传递给表达式。编译器进一步处理,允许我们忽略类型并自动替我们推断这些类型。如果你有2个或更多个参数,你需要用圆括号:(x,y)=>。如果只有一个,你设置不需要这样:x=>。

static void Main(string[] args) {
    Func
<bool, int> del = x => x ? 10 : 5;
    
new ShoppingCart().Process(del);
}
// 更短啦...
static void Main(
string[] args) {
    
new ShoppingCart().Process(x => x ? 10 : 5);
}

 

  就是这样子。x被推断为bool型,并且有返回值,因为Process接收一个Func。如果我们想实现像之前那样的完整代码块,我们只需要加上大括号。

static void Main(string[] args) {
    
new ShoppingCart().Process(  x => {
        
int discount = 0;
        
if (DateTime.Now.Hour < 12) {
            discount
= 5;
        }
        
else if (DateTime.Now.Hour < 20) {
            discount
= 10;
        }
        
else if(x)  {
            discount
= 20;
        }
        
else {
             discount
= 15;
        }
        return discount;
    });
}

 

  写在最后

  使用与不使用大括号有一个重要的不同。当你用时,你创建一个“语句Lambda”,反之,它是"表达Lambda"。语句Lambda能执行多条语句(因此需要大括号),但不能创建表达树。你可能只在使用IQueryable接口是遇到这个问题。下面的例子说明这个问题。

List<string> list = new List<string>();
IQueryable
<string> query = list.AsQueryable();
list.Add(
"one");
list.Add(
"two");
list.Add(
"three");

string foo = list.First(x => x.EndsWith("o"));
string bar = query.First(x => x.EndsWith("o"));
// foo and bar are now both 'two' as expected
foo = list.First(x => { return x.EndsWith("e"); }); //no error
bar
= query.First(x => { return x.EndsWith("e"); }); //error
bar
= query.First((Func<string,bool>)(x => { return x.EndsWith("e"); })); //no error

 

  倒数第二行在编译时失败。这是因为IQueryable.First期望得到一个表达式作为参数,然而List.First期望得到一个委托。你可以按照最后一行强制转换Lambda到一个委托(使用First的方法重载)。

  这里很难结束讨论,但是我觉得必须停止。Lambda大体上分为两类:一类创建匿名方法和委托;另一类创建表达式。表达式自成一体,并不是.NET开发者的必备知识(无疑在LINQ中已有实现)。

0
相关文章