Dot-Net
為什麼 lambda 表達式參數在 Func 和 Expression<Func> 之間不明確?
假設我有一堂課:
class MyClass { public int MyMethod(Func<int, int> f) { return 0; } public int MyMethod(Expression<Func<int, int>> f) { return 1; } }當我嘗試使用 lambda 表達式呼叫該方法時,我收到一個編譯錯誤,指出兩個重載之間的呼叫不明確:
var myClass = new MyClass(); myClass.MyMethod(x => 1 + x); // Error!當然,使用顯式類型呼叫可以正常工作:
myClass.MyMethod((Func<int, int>)(x => 1 + x)); // OK, returns 0 myClass.MyMethod((Expression<Func<int, int>>)(x => 1 + x)); // OK, returns 1表達式樹包含更多資訊(實際程式碼),我可能想在可用時使用這些資訊。但我也希望我的程式碼與代表一起工作。不幸的是,這種模棱兩可使得我必須找到另一種方法來區分這兩個呼叫,這會弄亂原本乾淨的 API。
C# 規範沒有說明這種特定情況,因此從這個意義上說,行為確實符合規範。
但是,有一個論點是表達式樹應該優先於委託。該
Compile方法充當從表達式樹到委託的顯式轉換。表達式樹包含更多資訊,當您編譯為委託時,您會失去該資訊。在另一個方向沒有轉換。有什麼理由不喜歡表達式樹嗎?
回答標題上的問題,它們是模棱兩可的,因為類型系統沒有“lambda 表達式”的概念。這是一個編譯器功能,可以轉換為委託或表達式樹,因此您需要明確要轉換為哪種類型。大多數時候,由於使用 lambda 表達式的上下文,編譯器也會自動推斷目標。例如,在
IEnumerable擴展方法中使用 lambda 與在擴展方法中使用 lambdaIQueryable。現在,要回答為什麼不總是更喜歡表達式樹的問題,您有 MagnatLU 已經陳述的性能論點。如果您接受一個
Expression然後呼叫Compile以能夠執行它,那麼與接受委託相比,它總是會慢一些。兩者之間也存在語義差異,委託只是執行某些程式碼的一種方式,而表達式樹是對實際程式碼的描述。
如果我是你,我會選擇將接受表達式的方法的名稱更改為清楚地反映它對基於委託的方法所做的額外操作。