呃,先不要砸雞蛋。我說的Lambda運算式指的是Expression<T>,不是隨便哪個item => { ... }。好吧,如果你還是不明白,那麼我給你一個例子:
(抱歉,原來的return item;是個筆誤,現已更正為 return (item%2) == 0;
代碼
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. )
代碼
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的寫法:
(補充,有人說不醜啊,比之前給出的方案沒有多出幾個字元。問題是,醜不醜不僅僅是你鼻子長了多少個的問題,地方不對照樣很醜。這裡面的醜在於,代碼的表面含義是“產生一個新對象,並初始化某些屬性”,但實際上並非這個含義,而是修改一些已有對象的某幾個屬性。“一個”和“一些”,“新對象”和“修改”都是不吻合的地方,我認為這就是醜。)
代碼
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的事情了,問題是我這裡壓根沒打算探討這個問題。)