我在.net 4.7中使用新的元组值类型。在这个例子中,我试图为一个或多个元组的情况做一个switch语句:
using System;
namespace ValueTupleTest
{
class Program
{
static void Main(string[] args)
{
(char letterA, char letterB) _test = ('A','B');
Console.WriteLine($"Letter A: '{_test.letterA}', Letter B: '{_test.letterB}'");
switch (_test)
{
case ('A', 'B'):
Console.WriteLine("Case ok.");
break;
}
}
}
}
不幸的是,这不会编译。
如何正确处理元组并在switch语句中创建案例?
答案 0 :(得分:8)
从技术上回答您的问题,您可以使用when
来检查元组的值:
(char letterA, char letterB) _test = ('A', 'B');
Console.WriteLine($"Letter A: '{_test.letterA}', Letter B: '{_test.letterB}'");
switch (_test)
{
case var tuple when tuple.letterA == 'A' && tuple.letterB == 'B':
Console.WriteLine("Case ok.");
break;
case var tuple when tuple.letterA == 'D' && tuple.letterB == '\0':
Console.WriteLine("Case ok.");
break;
}
但是,请考虑使用if
版本,因为它可能是一个更易阅读且易于理解的解决方案。
此问题的另一方面是单一责任。您的方法知道A
和B
,D
和\0
字符是什么意思打破了单一责任原则。
就OOP而言,最好将这些知识与主代码分离为一个单独的方法
这样的事情可以使代码更清洁:
private static bool IsCaseOk(char a, char b)
{
return (a == 'A' && b == 'B') || (a == 'D' && b == '\0'); // any logic here
}
public static void Main()
{
(char letterA, char letterB) _test = ('A', 'B');
Console.WriteLine($"Letter A: '{_test.letterA}', Letter B: '{_test.letterB}'");
if (IsCaseOk(_test.letterA, _test.letterB)) {
Console.WriteLine("Case ok.");
} else {
Console.WriteLine("Case not ok.");
}
}
如果这些字母在您的域中有任何意义,那么最好甚至创建一个具有两个char
属性的类并在那里封装该逻辑。
答案 1 :(得分:6)
C#7.3引入了元组相等性,这意味着你在问题中的初步想法几乎是正确的。您只需捕获您正在比较的值:
var _test = ('A','B');
switch (_test)
{
case var t when t == ('A', 'B'):
Console.WriteLine("Case ok.");
break;
}
答案 2 :(得分:5)
使用元组或模式匹配没有错。如果有的话,这些允许您编写更清晰的代码并避免将您的逻辑分散到多个方法。
C#7不允许您匹配尚未的元组值。您无法将两个元组与==
运算符进行比较。您可以做的是,使用Equals
两个比较两个值元组:
if (_test.Equals(('A','B'))
{
Console.WriteLine("Case A ok.");
}
else if (_test.Equals(('D','\0'))
{
Console.WriteLine("Case D ok.");
}
您似乎正在尝试为解析器(?)创建状态匹配,以匹配特定模式。如果指定不同的状态类而不是对所有情况使用单个元组,则此可以使用模式匹配。
您需要做的就是指定一个没有方法的IState接口,并在所有状态类中使用它,例如:
interface IMyState {};
public class StateA:IMyState{ public string PropA{get;set;} };
public class StateD:IMyState{ public string PropD{get;set;} };
...
IMyState _test= new StateD(...);
switch (_test)
{
case StateA a:
Console.WriteLine($"Case A ok. {a.PropA}");
break;
case StateD d:
Console.WriteLine($"Case D ok. {d.PropD}");
break;
default :
throw new InvalidOperationException("Where's my state ?");
}
a
,d
变量是强类型的,这意味着您不必向IState
接口添加任何内容。它只是为了满足编译器。
通过使用结构而不是状态类型的类,您将获得与元组相同的内存优势。如果要使用解构,可以为每种类型添加Deconstruct
方法,或在单独的静态类中使用Deconstruct
扩展方法。
答案 3 :(得分:2)
感谢您的回复。
我决定放弃使用switch语句并转到旧的if / else语句。
using System;
namespace ValueTupleTest
{
class Program
{
static void Main(string[] args)
{
(char letterA, char letterB) _test = ('A','B');
Console.WriteLine($"Letter A: '{_test.letterA}', Letter B: '{_test.letterB}'");
if (_test.letterA == 'A' && _test.letterB == 'B')
{
Console.WriteLine("Case A ok.");
}
else if (_test.letterA == 'D' && _test.letterB == '\0')
{
Console.WriteLine("Case D ok.");
}
}
}
}
这样我可以决定是否要测试元组中的所有值以及我需要的顺序。我认为它在性能上应该没有太大差异。
如果有另一种方法使用带有switch语句的元组,请随意举例。
答案 4 :(得分:1)
case (...):
的语法保留给将来的各种模式。请参阅C#语言功能规范中描述的位置模式:https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md#positional-pattern
答案 5 :(得分:1)
为任何偶然发现此问题的人提供一个便条。
C#8.0引入了switch expressions,它在这种情况下非常有用。
现在您可以执行以下操作:
var test = ('A', 'B');
test switch
{
('A', 'B') => Console.WriteLine("OK"),
('A', _) => Console.WriteLine("First part OK"),
( _, 'B') => Console.WriteLine("Second part OK"),
_ => Console.WriteLine("Not OK"),
};