在System.Linq.Expressions中切换没有案例(但默认情况下)

Via*_*nov 11 .net c# linq-expressions

我试图用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);
Run Code Online (Sandbox Code Playgroud)

但在最后一行我得到一个ArgumentException:

需要非空收集.参数名称:案例

这个例外的原因是什么?这可能是Expression.Switch(...)中的一个错误?

在C#中,只有"默认"部分的开关是正确的:

switch(expr) {
default:
  return 0;
}//switch
Run Code Online (Sandbox Code Playgroud)

UPD:我已经向GitHub上的CoreFX回购提交了一个问题

Jon*_*nna 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
Run Code Online (Sandbox Code Playgroud)

这里SwitchExpression返回一个值switch不能做的值.

所以,正如能够做某事SwitchExpression并不意味着你可以用a来做switch,所以也没有理由假设能够用一种switch方法做某事你可以用a做SwitchExpression.

也就是说,我认为没有任何理由可以这样SwitchExpression设置,除非它简化了表达式没有案例没有默认主体的情况.也就是说,我认为这可能仅仅是表达通常意图包含多个案例的问题,而这正是它被编码为支持的问题.

我已经向.NET Core提交了一个pull-request,允许这样的无表达式,通过生成一个SwitchExpression类型的默认值switchValue与默认主体具有相同主体的位置.这种方法意味着任何SwitchExpression在没有案例的情况下都会感到惊讶的事情应该仍然应对,避免向后兼容性问题.那里是没有违约的情况下,或者是通过创建一个空操作,表情,什么也不做,所以现在仍然抛出的唯一情况处理的ArgumentException是,如果不存在的情况下,并没有默认类型是明确设置为其他的东西void,这案件在打字规则下无效,显然仍然必须保留.

[更新:这种方法被拒绝了,但后来的拉取请求被接受了,所以SwitchExpression.NET Core现在允许无案例,但是如果其他版本的.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.
  );
}
Run Code Online (Sandbox Code Playgroud)

超载如:

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

然后可以添加等等.

这导致Expression整体修剪器比我的拉动请求,因为它switch在无案例情况下完全切断并且只返回默认体.如果你真的需要一个,SwitchExpression那么你可以创建一个类似的辅助方法,它遵循与pull-request在创建new SwitchCase然后使用它时所做的相同的逻辑.