2015-02-06 60 views
11

我試圖創建System.Linq.Expressions開關表達式:切換,而在病例System.Linq.Expressions(但默認)

var value = Expression.Parameter(typeof(int)); 
var defaultBody = Expression.Constant(0); 
var cases1 = new[] { Expression.SwitchCase(Expression.Constant(1), Expression.Constant(1)), }; 
var cases2 = new SwitchCase[0]; 
var switch1 = Expression.Switch(value, defaultBody, cases1); 
var switch2 = Expression.Switch(value, defaultBody, cases2); 

但在最後一行我得到一個ArgumentException:

需要非空集合。參數名稱:案例

這個例外的原因是什麼?可能是這個錯誤Expression.Switch(…)

在C#與 「默認」 部分交換機不僅是正確的:

switch(expr) { 
default: 
    return 0; 
}//switch 

UPD:我已提交an issue到CoreFX回購GitHub上

+2

這樣的建築的目標是什麼? ''開關''默認',沒有'case'只是執行'默認' – 2015-02-06 07:43:00

+0

對於我來說,沒有案例的切換看起來很沒意義​​,所以我認爲這個例外是合理的。 – 2015-02-06 07:43:01

+0

是的,C#規範說一個開關塊有零個或多個開關部分;但這並不意味着開關表達式必須與C#規範混淆。由於您在運行時創建了表達式,因此您可以作爲解決方法簡單地添加一個「Expression.SwitchCase」,其值爲!=開關值;或者將默認情況下的主體添加爲開關值,其值爲開關值。 – sloth 2015-02-06 11:25:27

回答

6

還沒有一個完整的比喻在C#的switchSwitchExpression之間。在另一個方向上,認爲你可以有:

var value = Expression.Parameter(typeof(int)); 
var meth = Expression.Lambda<Func<int, string>>(
    Expression.Switch(
    value, 
    Expression.Call(value, typeof(object).GetMethod("ToString")), 
    Expression.SwitchCase(Expression.Constant("Zero"), Expression.Constant(0, typeof(int))), 
    Expression.SwitchCase(Expression.Constant("One"), Expression.Constant(1, typeof(int)))), 
    value 
).Compile(); 
Console.WriteLine(meth(0)); // Zero 
Console.WriteLine(meth(1)); // One 
Console.WriteLine(meth(2)); // 2 

這裏SwitchExpression返回一個值,這是一件好事switch不能做。

因此,正如能夠做一些事情SwitchExpression並不意味着你可以用switch做,所以也沒有理由認爲能夠做什麼用switch意味着你可以用SwitchExpression做。

這麼說,我明白爲什麼SwitchExpression也許是這樣設置的,不同之處在於它簡化了其中一個表達式沒有的情況下沒有默認的身體情況沒有充分的理由。也就是說,我認爲這可能只是表達一般意圖有多個案件的表達問題,而這正是編碼要支持的內容。

我已經submitted a pull-request to .NET Core將允許這樣的情況下,較少的表達式,通過產生SwitchExpression其中用於的switchValue的類型的默認值具有相同的主體作爲默認體。這種方法意味着任何會被SwitchExpression感到驚訝的事情,沒有任何情況下仍然可以應付,避免向後兼容性問題。沒有缺省的情況通過創建noop表達式來處理,因此現在仍然拋出ArgumentException的唯一情況是如果沒有情況沒有缺省該類型被明確地設置爲除了void之外的東西,這種情況在打印規則中是無效的,這些打印規則顯然仍然應該保留。

[更新:這種做法遭到了拒絕,但a later pull-request被接受,所以現在無外殼SwitchExpression S被.NET核心允許的,但如果當由.NET的其他版本採用的是另一回事。

與此同時,或者如果您使用另一個版本的。NET,你最好關閉使用一個輔助方法,如:

public static Expression SwitchOrDefault(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable<SwitchCase> cases) 
{ 
    if (cases != null) 
    { 
    // It's possible that cases is a type that can only be enumerated once. 
    // so we check for the most obvious condition where that isn't true 
    // and otherwise create a ReadOnlyCollection. ReadOnlyCollection is 
    // chosen because it's the most efficient within Switch itself. 
    if (!(cases is ICollection<SwitchCase>)) 
     cases = new ReadOnlyCollection<SwitchCase>(cases); 
    if (cases.Any()) 
     return Switch(type, switchValue, defaultBody, comparison, cases); 
    } 
    return Expression.Block(
    switchValue, // include in case of side-effects. 
    defaultBody != null ? defaultBody : Expression.Empty() // replace null with a noop expression. 
); 
} 

過載,如:

public static Expression SwitchOrDefault(Expression switchValue, Expression defaultBody, params SwitchCase[] cases) 
{ 
    return SwitchOrDefault(switchValue, defaultBody, null, (IEnumerable<SwitchCase>)cases); 
} 

等等然後可以加入。

這會導致一個整數Expression總比我的拉動請求,因爲它完全刪除了switch在無情況下,只是返回默認正文。如果你確實需要有一個SwitchExpression那麼你可以創建一個類似的幫助方法,該方法遵循與創建新的SwitchCase然後使用它的請求相同的邏輯。