Ответ 1
Не существует полной аналогии между С# 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
был установлен таким образом, за исключением того, что, возможно, это упрощает случай, когда выражение не имеет случаев и без тела по умолчанию. Тем не менее, я думаю, что это, скорее всего, просто вопрос выражения, которое обычно предназначено для нескольких случаев, и именно это было закодировано для поддержки.
Я отправил pull-request в .NET Core, который позволил бы использовать такие безрезультатные выражения, создав SwitchExpression
где значение по умолчанию для типа switchValue
имеет ту же структуру, что и тело по умолчанию. Этот подход означает все, что было бы удивлено SwitchExpression
, при этом ни один случай не должен справляться, избегая проблем с обратной совместимостью. Случай с отсутствием дефолта либо обрабатывается созданием noop-выражения, которое ничего не делает, поэтому единственный случай, который теперь все еще бросает ArgumentException
, - это если нет аргумента и по умолчанию и, тип явно задан чем-то другим, кроме void
, этот случай является недопустимым в соответствии с правилами ввода, которые, очевидно, должны сохраняться.
[Обновление: этот подход был отклонен, но был принят более поздний запрос на тягу, поэтому без-case SwitchExpression
теперь разрешено .NET Core, хотя если и когда это принято другими версиями .NET, это другое дело].
В то же время, или если вы используете другую версию .NET, вы можете использовать хелпер-метод, например:
public static Expression SwitchOrDefault(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, IEnumerable<SwitchCase> cases)
{
if (cases != null)
{
// It 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 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, потому что он полностью исключает switch
в случайном случае и просто возвращает тело по умолчанию. Если у вас действительно должен быть SwitchExpression
, тогда вы можете создать похожий вспомогательный метод, который будет следовать той же логике, что и запрос pull-запроса при создании нового SwitchCase
, а затем используя это.