我尝试使用System.Linq.Expressions创建一个switch表达式:
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提交给GitHub上的CoreFX回购
答案 0 :(得分:6)
C#的switch
和SwitchExpression
之间没有完全类比。在另一个方向,考虑你可以:
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被接受了,所以.NET Core现在允许无案SwitchExpression
,但是当其他版本的时候采用它时。 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
整体比我的pull-request,因为它在无案例情况下完全删除了switch
并且只返回默认正文。如果您真的需要SwitchExpression
,那么您可以创建一个类似的辅助方法,其方法与pull-request在创建新SwitchCase
时所使用的逻辑相同,然后使用这一点。