System.Linq.Expressions 中不带大小写的开关(但带默认值)
Switch without cases (but with default) in System.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# 中,只有 "default" 部分的开关是正确的:
switch(expr) {
default:
return 0;
}//switch
UPD:我已在 GitHub
上将 an issue 提交到 CoreFX 仓库
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
returns 的值是 switch
做不到的。
所以,正如能够用 SwitchExpression
做某事并不意味着你可以用 switch
做某事一样,也没有理由假设能够用 switch
做某事switch
表示您可以使用 SwitchExpression
.
就是说,我看不出为什么 SwitchExpression
是这样设置的,除了它可能简化了表达式没有大小写的情况 和 没有默认值body。话虽如此,我认为这可能只是表达式通常用于多个案例的问题,而这正是它被编码支持的内容。
我已经 submitted a pull-request to .NET Core 允许这样的 case-less 表达式,方法是生成一个 SwitchExpression
,其中 switchValue
类型的默认值具有与默认值 body 相同的 body。这种方法意味着任何对没有案例的 SwitchExpression
感到惊讶的事情仍然应该处理,避免 backwards-compatibility 问题。没有默认值的情况是通过创建一个什么都不做的 noop 表达式来处理的,所以现在唯一仍然抛出 ArgumentException
的情况是如果没有 and 没有默认值 和 类型被显式设置为 void
以外的其他类型,这种情况在显然仍然必须保留的打字规则下是无效的。
[更新:该方法被拒绝,但 a later pull-request 被接受,因此 .NET Core 现在允许 case-less SwitchExpression
s,尽管如果以及何时被采用.NET 的其他版本另当别论。
同时,或者如果您使用另一个版本的 .NET,您将 best-off 使用如下辅助方法:
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-cases 的情况下完全切掉了 switch
而只是 returns默认 body。如果您 真的 需要一个 SwitchExpression
那么您可以创建一个类似的辅助方法,它遵循与 pull-request 在创建一个新的 [=31] 时相同的逻辑=] 然后使用它。
我尝试用 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# 中,只有 "default" 部分的开关是正确的:
switch(expr) {
default:
return 0;
}//switch
UPD:我已在 GitHub
上将 an issue 提交到 CoreFX 仓库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
returns 的值是 switch
做不到的。
所以,正如能够用 SwitchExpression
做某事并不意味着你可以用 switch
做某事一样,也没有理由假设能够用 switch
做某事switch
表示您可以使用 SwitchExpression
.
就是说,我看不出为什么 SwitchExpression
是这样设置的,除了它可能简化了表达式没有大小写的情况 和 没有默认值body。话虽如此,我认为这可能只是表达式通常用于多个案例的问题,而这正是它被编码支持的内容。
我已经 submitted a pull-request to .NET Core 允许这样的 case-less 表达式,方法是生成一个 SwitchExpression
,其中 switchValue
类型的默认值具有与默认值 body 相同的 body。这种方法意味着任何对没有案例的 SwitchExpression
感到惊讶的事情仍然应该处理,避免 backwards-compatibility 问题。没有默认值的情况是通过创建一个什么都不做的 noop 表达式来处理的,所以现在唯一仍然抛出 ArgumentException
的情况是如果没有 and 没有默认值 和 类型被显式设置为 void
以外的其他类型,这种情况在显然仍然必须保留的打字规则下是无效的。
[更新:该方法被拒绝,但 a later pull-request 被接受,因此 .NET Core 现在允许 case-less SwitchExpression
s,尽管如果以及何时被采用.NET 的其他版本另当别论。
同时,或者如果您使用另一个版本的 .NET,您将 best-off 使用如下辅助方法:
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-cases 的情况下完全切掉了 switch
而只是 returns默认 body。如果您 真的 需要一个 SwitchExpression
那么您可以创建一个类似的辅助方法,它遵循与 pull-request 在创建一个新的 [=31] 时相同的逻辑=] 然后使用它。