c# - 在 System.Linq.Expressions 中不带大小写(但使用默认值)切换

标签 c# .net 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);

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

Non-empty collection required. Parameter name: cases

这个异常的原因是什么?这可能是 Expression.Switch(…) 中的错误?

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

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

UPD:我已经提交了an issue到 GitHub 上的 CoreFX 存储库

最佳答案

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 做它一样,所以也没有理由假设能够do something with a switch 意味着您可以使用 SwitchExpression 来完成。

就是说,我看不出为什么要这样设置 SwitchExpression,除了它简化了表达式没有大小写 没有默认主体的情况。话虽如此,我认为这可能只是表达通常旨在具有多个案例的问题,而这正是它被编码支持的内容。

我已经 submitted a pull-request to .NET Core这将允许这样的无大小写表达式,通过生成一个 SwitchExpression,其中 switchValue 类型的默认值与默认主体具有相同的主体。这种方法意味着任何对没有案例的 SwitchExpression 感到惊讶的事情仍然应该处理,避免向后兼容问题。没有默认值的情况是通过创建一个什么都不做的 noop 表达式来处理的,所以现在仍然抛出 ArgumentException 的唯一情况是没有 and 没有默认值并且类型被显式设置为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 更精简,因为它在 no-case 情况下完全切掉了 switch 并且只返回默认主体。如果您真的需要一个SwitchExpression,那么您可以创建一个类似的辅助方法,它遵循与拉取请求在创建新的SwitchCase< 时相同的逻辑 然后使用它。

关于c# - 在 System.Linq.Expressions 中不带大小写(但使用默认值)切换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28360782/

相关文章:

c# - 参数 'name' 不能为 null、空或仅包含空格

c# - 如何使用智能卡

c# - 过滤器 IQueryable 的动态表达式

c# - 使用 C# 预处理器添加引用

c# - DEVEXPRESS - xtrareport - 分页符

c# - C# 中的只读列表

c# - 从另一个类更改一个类的属性

c# - CRM 创建 CrmConnection 的新实例

c# - 如何在 EF 4.3 中使用具有复杂键的 AddOrUpdate 播种数据

c# - 可靠地检测 C# 表达式树中编译器生成的类