有没有办法在不重复声明 case value:
的情况下通过多个案例陈述?
我知道这有效:
switch (value)
{
case 1:
case 2:
case 3:
// Do some stuff
break;
case 4:
case 5:
case 6:
// Do some different stuff
break;
default:
// Default stuff
break;
}
但我想做这样的事情:
switch (value)
{
case 1,2,3:
// Do something
break;
case 4,5,6:
// Do something
break;
default:
// Do the Default
break;
}
这是我从不同语言中想到的语法,还是我遗漏了什么?
,
添加了另一种含义,并且不与任何其他 c 样式语言共享。这对我来说似乎更脏。
我想这已经回答了。但是,我认为您仍然可以通过以下方式以语法更好的方式混合这两个选项:
switch (value)
{
case 1: case 2: case 3:
// Do Something
break;
case 4: case 5: case 6:
// Do Something
break;
default:
// Do Something
break;
}
您提到的第二种方法在 C++ 和 C# 中都没有语法。
你的第一种方法没有问题。但是,如果您有很大的范围,只需使用一系列 if 语句。
C# 7 的原始答案
在 C# 7(在 Visual Studio 2017/.NET Framework 4.6.2 中默认可用)中,现在可以使用 switch statement 进行基于范围的切换,这将有助于解决 OP 的问题。
例子:
int i = 5;
switch (i)
{
case int n when (n >= 7):
Console.WriteLine($"I am 7 or above: {n}");
break;
case int n when (n >= 4 && n <= 6 ):
Console.WriteLine($"I am between 4 and 6: {n}");
break;
case int n when (n <= 3):
Console.WriteLine($"I am 3 or less: {n}");
break;
}
// Output: I am between 4 and 6: 5
笔记:
括号 ( 和 ) 在 when 条件中不是必需的,但在此示例中用于突出显示比较。
var 也可以用来代替 int。例如:case var n when n >= 7:。
更新了 C# 9 的示例
switch(myValue)
{
case <= 0:
Console.WriteLine("Less than or equal to 0");
break;
case > 0 and <= 10:
Console.WriteLine("More than 0 but less than or equal to 10");
break;
default:
Console.WriteLine("More than 10");
break;
}
或者
var message = myValue switch
{
<= 0 => "Less than or equal to 0",
> 0 and <= 10 => "More than 0 but less than or equal to 10",
_ => "More than 10"
};
Console.WriteLine(message);
此语法来自 Visual Basic Select...Case Statement:
Dim number As Integer = 8
Select Case number
Case 1 To 5
Debug.WriteLine("Between 1 and 5, inclusive")
' The following is the only Case clause that evaluates to True.
Case 6, 7, 8
Debug.WriteLine("Between 6 and 8, inclusive")
Case Is < 1
Debug.WriteLine("Equal to 9 or 10")
Case Else
Debug.WriteLine("Not between 1 and 10, inclusive")
End Select
您不能在 C# 中使用此语法。相反,您必须使用第一个示例中的语法。
您可以省略换行符,它为您提供:
case 1: case 2: case 3:
break;
但我认为这种糟糕的风格。
C#9 出现了关系模式匹配。这使我们能够:
switch (value)
{
case 1 or 2 or 3:
// Do stuff
break;
case 4 or 5 or 6:
// Do stuff
break;
default:
// Do stuff
break;
}
In deep tutorial of Relational Patter in C#9
Pattern-matching changes for C# 9.0
关系模式允许程序员表达输入值与常量值相比必须满足关系约束
.NET Framework 3.5 有范围:
您可以将它与“包含”和 IF 语句一起使用,因为就像有人说 SWITCH 语句使用“==”运算符一样。
这里有一个例子:
int c = 2;
if(Enumerable.Range(0,10).Contains(c))
DoThing();
else if(Enumerable.Range(11,20).Contains(c))
DoAnotherThing();
但我认为我们可以玩得更开心:因为你不需要返回值并且这个动作不带参数,你可以很容易地使用动作!
public static void MySwitchWithEnumerable(int switchcase, int startNumber, int endNumber, Action action)
{
if(Enumerable.Range(startNumber, endNumber).Contains(switchcase))
action();
}
使用这种新方法的旧示例:
MySwitchWithEnumerable(c, 0, 10, DoThing);
MySwitchWithEnumerable(c, 10, 20, DoAnotherThing);
由于您传递的是操作而不是值,因此您应该省略括号,这非常重要。如果您需要带参数的函数,只需将 Action
的类型更改为 Action<ParameterType>
。如果您需要返回值,请使用 Func<ParameterType, ReturnType>
。
在 C# 3.0 中,没有简单的 Partial Application 来封装 case 参数相同的事实,但是您创建了一个小辅助方法(虽然有点冗长)。
public static void MySwitchWithEnumerable(int startNumber, int endNumber, Action action){
MySwitchWithEnumerable(3, startNumber, endNumber, action);
}
这是一个示例,说明新的功能导入语句如何比旧的命令式语句更强大和优雅。
int start
和 int count
。您的示例不会按照它们的编写方式正常工作。你把它写成第二个参数是 int end
。例如 - Enumerable.Range(11,20)
将产生 20 个以 11 开头的数字,而不是从 11 到 20 的数字。
Enumerable.Range(11,20).Contains(c)
等价于 for(int i = 11; i < 21; ++i){ if (i == c) return true; } return false;
如果您的范围很大,则需要很长时间,而仅使用 >
和 <
会快速且持续时间。
MySwitchWithEnumerable
返回 void
是针对这种情况的弱设计。原因:您已将 if-else
转换为一系列独立语句 - 隐藏了意图,即它们是互斥的 - 只有一个 action
被执行。而是返回 bool
,正文为 if (..) { action(); return true; } else return false;
调用站点然后显示意图:if (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));
。这是优选的。但是,对于这种简单的情况,它也不再是对原始版本的重大改进。
这是完整的 C# 7 解决方案...
switch (value)
{
case var s when new[] { 1,2,3 }.Contains(s):
// Do something
break;
case var s when new[] { 4,5,6 }.Contains(s):
// Do something
break;
default:
// Do the default
break;
}
它也适用于字符串......
switch (mystring)
{
case var s when new[] { "Alpha","Beta","Gamma" }.Contains(s):
// Do something
break;
...
}
下面的代码不起作用:
case 1 | 3 | 5:
// Not working do something
这样做的唯一方法是:
case 1: case 2: case 3:
// Do something
break;
您正在寻找的代码在 Visual Basic 中工作,您可以在其中轻松放入范围...在 switch
语句的 none
选项或 if else
块方便,我建议,在非常极端的情况下,使用 Visual Basic 制作 .dll 并导入回您的 C# 项目。
注意:Visual Basic 中的等效开关是 Select Case
。
另一种选择是使用例程。如果案例 1-3 都执行相同的逻辑,则将该逻辑包装在一个例程中并为每个案例调用它。我知道这实际上并没有摆脱案例陈述,但它确实实现了良好的风格并将维护降至最低......
[编辑] 添加替代实现以匹配原始问题...[/编辑]
switch (x)
{
case 1:
DoSomething();
break;
case 2:
DoSomething();
break;
case 3:
DoSomething();
break;
...
}
private void DoSomething()
{
...
}
Alt
switch (x)
{
case 1:
case 2:
case 3:
DoSomething();
break;
...
}
private void DoSomething()
{
...
}
在 C# 7 中,我们现在有 Pattern Matching,因此您可以执行以下操作:
switch (age)
{
case 50:
ageBlock = "the big five-oh";
break;
case var testAge when (new List<int>()
{ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 }).Contains(testAge):
ageBlock = "octogenarian";
break;
case var testAge when ((testAge >= 90) & (testAge <= 99)):
ageBlock = "nonagenarian";
break;
case var testAge when (testAge >= 100):
ageBlock = "centenarian";
break;
default:
ageBlock = "just old";
break;
}
C# 中 switch 的一个鲜为人知的方面是它依赖于 operator= 并且因为它可以被覆盖,所以你可以有这样的东西:
string s = foo();
switch (s) {
case "abc": /*...*/ break;
case "def": /*...*/ break;
}
gcc 实现了对 C 语言的扩展以支持顺序范围:
switch (value)
{
case 1...3:
//Do Something
break;
case 4...6:
//Do Something
break;
default:
//Do the Default
break;
}
编辑:刚刚注意到问题上的 C# 标签,所以大概 gcc 答案没有帮助。
其实我也不喜欢 GOTO 命令,但它在微软官方资料中,这里都是允许的语法。
如果 switch 部分的语句列表的端点是可到达的,则会发生编译时错误。这被称为“不失败”规则。这个例子
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
default:
CaseOthers();
break;
}
是有效的,因为没有开关部分具有可到达的端点。与 C 和 C++ 不同,switch 部分的执行不允许“落入”下一个 switch 部分,示例
switch (i) {
case 0:
CaseZero();
case 1:
CaseZeroOrOne();
default:
CaseAny();
}
导致编译时错误。当一个 switch 部分的执行之后要执行另一个 switch 部分时,必须使用显式 goto case 或 goto default 语句:
switch (i) {
case 0:
CaseZero();
goto case 1;
case 1:
CaseZeroOrOne();
goto default;
default:
CaseAny();
break;
}
一个开关部分允许有多个标签。这个例子
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
default:
CaseTwo();
break;
}
我相信在这种特殊情况下,可以使用 GOTO,它实际上是唯一的失败方法。
goto
几乎总是可以避免的(尽管我不认为它在这里“可怕” - 它正在扮演一个特定的、结构化的角色)。在您的示例中,因为您已将案例主体包装在函数中(一件好事),案例 0 可以成为 CaseZero(); CaseZeroOrOne(); break;
。不需要 goto
。
在 C# 8.0 中,您可以使用最适合您的情况的新 switch expression 语法。
var someOutput = value switch
{
>= 1 and <= 3 => <Do some stuff>,
>= 4 and <= 6 => <Do some different stuff>,
_ => <Default stuff>
};
如果您有大量字符串(或任何其他类型)都在做同样的事情,我建议使用字符串列表和 string.Contains 属性。
所以如果你有一个像这样的大 switch 语句:
switch (stringValue)
{
case "cat":
case "dog":
case "string3":
...
case "+1000 more string": // Too many string to write a case for all!
// Do something;
case "a lonely case"
// Do something else;
.
.
.
}
您可能希望将其替换为如下 if
语句:
// Define all the similar "case" string in a List
List<string> listString = new List<string>(){ "cat", "dog", "string3", "+1000 more string"};
// Use string.Contains to find what you are looking for
if (listString.Contains(stringValue))
{
// Do something;
}
else
{
// Then go back to a switch statement inside the else for the remaining cases if you really need to
}
这适用于任何数量的字符串情况。
我认为这个在 C# 7 或更高版本中更好。
switch (value)
{
case var s when new[] { 1,2 }.Contains(s):
// Do something
break;
default:
// Do the default
break;
}
您还可以在 C# switch case 中检查 Range:Switch case: can I use a range instead of a one number 或者如果您想了解 C# switch case 的基础知识
你也可以有完全不同的条件
bool isTrue = true;
switch (isTrue)
{
case bool ifTrue when (ex.Message.Contains("not found")):
case bool ifTrue when (thing.number = 123):
case bool ifTrue when (thing.othernumber != 456):
response.respCode = 5010;
break;
case bool ifTrue when (otherthing.text = "something else"):
response.respCode = 5020;
break;
default:
response.respCode = 5000;
break;
}
似乎已经投入了大量的工作来寻找使 C# 最少使用的语法之一以某种方式看起来更好或工作得更好的方法。就我个人而言,我发现 switch 语句很少值得使用。我强烈建议分析您正在测试的数据以及您想要的最终结果。
例如,假设您想快速测试已知范围内的值以查看它们是否为素数。您希望避免让您的代码进行无用计算,并且可以在线找到所需范围内的素数列表。您可以使用大量 switch 语句将每个值与已知的素数进行比较。
或者您可以创建一个素数数组映射并立即获得结果:
bool[] Primes = new bool[] {
false, false, true, true, false, true, false,
true, false, false, false, true, false, true,
false,false,false,true,false,true,false};
private void button1_Click(object sender, EventArgs e) {
int Value = Convert.ToInt32(textBox1.Text);
if ((Value >= 0) && (Value < Primes.Length)) {
bool IsPrime = Primes[Value];
textBox2.Text = IsPrime.ToString();
}
}
也许您想查看字符串中的字符是否为十六进制。你可以使用一个笨拙且有点大的 switch 语句。
或者您可以使用正则表达式来测试 char 或使用 IndexOf 函数在已知的十六进制字母字符串中搜索 char:
private void textBox2_TextChanged(object sender, EventArgs e) {
try {
textBox1.Text = ("0123456789ABCDEFGabcdefg".IndexOf(textBox2.Text[0]) >= 0).ToString();
} catch {
}
}
假设您要根据 1 到 24 范围内的值执行 3 种不同操作中的一种。我建议使用一组 IF 语句。如果这变得太复杂(或者数字更大,例如 5 个不同的操作,具体取决于 1 到 90 范围内的值),则使用枚举来定义操作并创建枚举的数组映射。然后该值将用于索引数组映射并获取所需操作的枚举。然后使用一小组 IF 语句或一个非常简单的 switch 语句来处理生成的枚举值。
此外,将一系列值转换为操作的数组映射的好处在于它可以通过代码轻松更改。使用硬连线代码,您无法在运行时轻松更改行为,但使用数组映射很容易。
一种更漂亮的处理方式
if ([4, 5, 6, 7].indexOf(value) > -1)
//Do something
您可以对具有相同结果的多个值执行此操作
只是为了添加到对话中,使用 .NET 4.6.2 我还能够执行以下操作。我测试了代码,它确实对我有用。
您还可以执行多个“OR”语句,如下所示:
switch (value)
{
case string a when a.Contains("text1"):
// Do Something
break;
case string b when b.Contains("text3") || b.Contains("text4") || b.Contains("text5"):
// Do Something else
break;
default:
// Or do this by default
break;
}
您还可以检查它是否与数组中的值匹配:
string[] statuses = { "text3", "text4", "text5"};
switch (value)
{
case string a when a.Contains("text1"):
// Do Something
break;
case string b when statuses.Contains(value):
// Do Something else
break;
default:
// Or do this by default
break;
}
我们也可以使用这种方法来实现 switch 语句中的多个情况......您可以使用这种方法使用任意数量的条件..
int i = 209;
int a = 0;
switch (a = (i>=1 && i<=100) ? 1 : a){
case 1:
System.out.println ("The Number is Between 1 to 100 ==> " + i);
break;
default:
switch (a = (i>100 && i<=200) ? 2 : a) {
case 2:
System.out.println("This Number is Between 101 to 200 ==> " + i);
break;
default:
switch (a = (i>200 && i<=300) ? 3 : a) {
case 3:
System.out.println("This Number is Between 201 to 300 ==> " + i);
break;
default:
// You can make as many conditions as you want;
break;
}
}
}
为此,您将使用 goto 语句。如:
switch(value){
case 1:
goto case 3;
case 2:
goto case 3;
case 3:
DoCase123();
//This would work too, but I'm not sure if it's slower
case 4:
goto case 5;
case 5:
goto case 6;
case 6:
goto case 7;
case 7:
DoCase4567();
}
goto
。更糟糕的是,完全没有必要使用 goto
,因为 OP 声明的原始语法确实有效。问题是是否有一种更简洁的方式来给出替代案例。正如人们比您早回答的那样,是的 - 如果您愿意将几个案例放在一行 case 1: case 2:
上,并且编辑器的自动样式允许。
不定期副业成功案例分享