为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)

时间:2022-02-26 14:41:35

呃,先不要砸鸡蛋。我说的Lambda表达式指的是Expression<T>,不是随便哪个item => { ... }。好吧,如果你还是不明白,那么我给你一个例子:

(抱歉,原来的return item;是个笔误,现已更正为 return (item%2) == 0; 

为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)代码
IQueryable < int >  values  =   new   int []{ 1 }.AsQueryable();
int  last  =   0 ;
var query 
=  values.Where(
    item 
=>  {
        last 
=  item;
        Console.WriteLine(
" Last value ={0} " , last);
        
return  (item%2) == 0;
    }
);
var result 
=  query.ToArray();
            

 

只要你在C# 4.0及以下版本跑,都会编译不通过。想知道编译器提示什么,请自己动手做实验。

(补充说明,我知道3.5里面没有BlockExpression等, 这些事Fx 4.0里面才加上的,我的意思是,为啥C# 4.0没有同步增加语法支持。有人说可能是没赶上,这个有点靠谱,不过到目前为止,我还是没有看到URL。希望知道的,手握资料的能贴出来分享一下。)

 

好了,问题来了:为什么不支持呢?这个问题暂时我没有找到什么答案,也许你能提供标准的答案。当然,我也有一些猜测:

1、和 item => item 这样的语法可能会有点冲突,不过也很好解决:只要限制statement都必须使用花括号即可;

2、可能有些QueryProvider无法支持执行语句,比如EntityFramework里面的ObjectQuery等。 不过我也没有觉得有什么太大问题,抛异常就好了。当然,这确实可能造成一些代码编译通过,但是执行的时候抛异常,你还不知道这样的查询是哪里构造出来的。就算是这样,那也只要尽早检查就能避免有问题的语句带病走太远。

 

不知道F#是否支持Lambda表达式中包含语句的情况,如果支持我都要想想是否转一个语言了,因为这对某些扩展造成了很大的困扰。比如说我本来构想是这样的:

(下面这部分代码又写错了,原来是CategoryId == XXX,少了 item. )

为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)代码
var query  =  from item  in  Pages
    
where  item.CategoryId  ==   1
    select item;
IQueryable
< int >  updateAffactedLine  =  query.Update(
    item 
=>  {
        item.CategoryId 
=  GetCategoryId(NewCategoryDropDownList);
        item.LastModifyDate = DateTime.Now;
     }

);


嗯,你也许会说,这怎么可能执行呢?呃,好吧,如果你不了解表达式Expression这个东西,也不了解表达式访问器ExpressionVisitor这个东西,赶紧关了这个帖子。如果你懂的话,我这么说一下你大概就明白了:

1、执行之前需要估值,比如自己写一个类似.NET 类库里面的Norminator、PartialEvaluator(参见EnumerableQuery里面的方法),这样就可以计算类似GetCategoryId(NewCategoryDropDownList)这样的表达式的实际值,变成一个ConstantExpression;

2、在经过估值之后,自己写一个Provider(或者用别的方法),将这个表达式树转换成以下Sql语句:

update Photo set CategoryId = @P0, LastModifyDate = @P1 where CategoryId = 1

 

现在因为C#不支持这样的语法,只能用一个很Ugly的写法:

(补充,有人说不丑啊,比之前给出的方案没有多出几个字符。问题是,丑不丑不仅仅是你鼻子长了多少个的问题,地方不对照样很丑。这里面的丑在于,代码的表面含义是“产生一个新对象,并初始化某些属性”,但实际上并非这个含义,而是修改一些已有对象的某几个属性。“一个”和“一些”,“新对象”和“修改”都是不吻合的地方,我认为这就是丑。) 

为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)为啥C#的Lambda表达式不支持语句?(无答案,你来回答。)代码
var query  =  from item  in  Pages
    
where  item.CategoryId  ==   1
    select item;
IQueryable
< int >  updateAffactedLine  =  query.UpdateUgly(
    () 
=>   new  Page(){
        CategoryId 
=  GetCategoryId(NewCategoryDropDownList),
        LastModifyDate 
=  DateTime.Now,
     }
);

 

上面那个写法Ugly就算了,有些时候有的问题还是解决不了,比如……算了,我觉得再说就太深了,你们会晕掉的。我们还是把本贴的焦点拉回来:

该死的C#为啥不支持Expression<T>中带语句的语法?大家热烈讨论一下吧。(为什么说不解决问题,我在后面的回复中会提到,不过深了我还不打算这里说清楚,这不是一句两句话就能说清楚的。)

 

(嗯,我每次想到这就烦,它阻碍了我的一些设计,不得不搞得很Ugly。然后接着就会想到,总有人对语言的优美视为XX,甚至喜欢把这些问题和Execution performance混到一块说……哦哦,又扯远了。) 

 

(补充:事实就是如此,又有人来说Execution performance的事情了,问题是我这里压根没打算探讨这个问题。)