如何编写一个开关表达式以支持返回相同结果的多种情况?
在版本8之前的c#中,开关可以这样写:
var switchValue = 3;
var resultText = string.Empty;
switch (switchValue)
{
case 1:
case 2:
case 3:
resultText = "one to three";
break;
case 4:
resultText = "four";
break;
case 5:
resultText = "five";
break;
default:
resultText = "unkown";
break;
}
当我使用c#版本8时,表达式语法是这样的:
var switchValue = 3;
var resultText = switchValue switch
{
1 => "one to three",
2 => "one to three",
3 => "one to three",
4 => "four",
5 => "five",
_ => "unknown",
};
所以我的问题是:如何将情况1、2和3转换为一个开关-case-arm,从而不需要重复该值?
根据“Rufus L”的建议更新:
对于我给出的例子,这是可行的。
var switchValue = 3;
var resultText = switchValue switch
{
var x when (x >= 1 && x <= 3) => "one to three",
4 => "four",
5 => "five",
_ => "unknown",
};
但这并不是我想要完成的。这仍然只是一种情况(带有筛选条件),而不是多种情况产生相同的右边结果。
c# 9支持以下功能:
var switchValue = 3;
var resultText = switchValue switch
{
1 or 2 or 3 => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
另外:
var switchValue = 3;
var resultText = switchValue switch
{
>= 1 and <= 3 => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
源
对于旧版本的c#,我使用以下扩展方法:
public static bool In<T>(this T val, params T[] vals) => vals.Contains(val);
是这样的:
var switchValue = 3;
var resultText = switchValue switch
{
var x when x.In(1, 2, 3) => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
它比x == 1 || x == 2 || x == 3更简洁,并且比new [] {1,2,3}. contains (x)的顺序更自然。
c# 9支持以下功能:
var switchValue = 3;
var resultText = switchValue switch
{
1 or 2 or 3 => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
另外:
var switchValue = 3;
var resultText = switchValue switch
{
>= 1 and <= 3 => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
源
对于旧版本的c#,我使用以下扩展方法:
public static bool In<T>(this T val, params T[] vals) => vals.Contains(val);
是这样的:
var switchValue = 3;
var resultText = switchValue switch
{
var x when x.In(1, 2, 3) => "one, two, or three",
4 => "four",
5 => "five",
_ => "unknown",
};
它比x == 1 || x == 2 || x == 3更简洁,并且比new [] {1,2,3}. contains (x)的顺序更自然。
我设法安装了它,但我还没有找到一种方法,可以用新语法为单个开关部分指定多个单独的大小写标签。
但是,你可以创建一个新变量来捕获值,然后使用一个条件来表示应该有相同结果的情况:
var resultText = switchValue switch
{
var x when
x == 1 ||
x == 2 ||
x == 3 => "one to three",
4 => "four",
5 => "five",
_ => "unknown",
};
如果你有很多用例要测试,这实际上更简洁,因为你可以在一行中测试一系列值:
var resultText = switchValue switch
{
var x when x > 0 && x < 4 => "one to three",
4 => "four",
5 => "five",
_ => "unknown",
};
如果你仍然在使用c# 8,所以不能像上面的答案一样使用c# 9 1或2或3 =>方法,你也可以用一种方式来避免创建额外的变量x,与接受的答案中的switchValue值相同(当然这是完全正确的,我只是建议一个替代方案,因为我不喜欢额外的x)。
var list = new List<int> { 3, 4, 5 };
var resultText = switchValue switch
{
1 => "one",
2 => "two",
_ when list.Contains(switchValue) => "three to five",
_ => "unknown",
};
显然,你可以用任意值替换_ when后面的条件,比如switchValue == 3 || switchValue == 4 || switchValue == 5;答案的重点是显示_ when位。
我只是认为_ when =>表示“其他时候”,_ =>表示“其他”。
遗憾的是,相对于switch-statement语法,这似乎是switch-expression语法中的一个缺点。正如其他帖子所建议的那样,相当笨拙的var语法是你唯一真正的选择。
所以你可能希望你可以这样写:
switchValue switch {
Type1 t1:
Type2 t2:
Type3 t3 => ResultA, // where the ResultX variables are placeholders for expressions.
Type4 t4 => ResultB,
Type5 t5 => ResultC
};
相反,你将需要编写下面这段相当尴尬的代码,使用typename:
switchValue switch {
var x when x is Type1 || x is Type2 || x is Type 3 => ResultA,
Type4 t4 => ResultB,
Type5 t5 => ResultC
};
在这样一个简单的例子中,您可能可以忍受这种尴尬。但是更复杂的例子就不太适用了。事实上,我的例子实际上是从我们自己的代码库中抽取的一个例子的简化,在那里我希望将一个switch-statement转换为一个switch-表达式,其中大约有六个结果,但有十多个类型情况。结果显然比switch语句可读性差。
我的观点是,如果switch-表达式需要共享结果,并且长度超过几行,那么你最好坚持使用switch-statement。嘘!虽然比较啰嗦,但可能是对队友的一种善意。
ResultType tmp;
switch (switchValue) {
case Type1 t1:
case Type2 t2:
case Type3 t3:
tmp = ResultA;
break;
case Type4 t4:
tmp = ResultB;
break;
case Type5 t5:
tmp = ResultC;
break;
};
return tmp;