如何在switch语句中使用c#tuple值类型

Sof*_*lan 8 c# tuples switch-statement c#-7.0

我在.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;
            }

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不编译.

如何正确地使用元组并在switch语句中创建案例?

Yel*_*yev 10

从技术上回答你的问题,你可以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;
}
Run Code Online (Sandbox Code Playgroud)

但是,请考虑使用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.");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果这些字母在你的域中有任何意义,那么甚至可能更好的想法是创建一个具有两个char属性的类并在那里封装这个逻辑.

  • 模式匹配是一种功能,而不是OOP概念.匹配一个值是完全可以的,不会破坏SRP.检查[匹配表达式]中的"绑定到值"部分(https://fsharpforfunandprofit.com/posts/match-expression/).如果有的话,这可以更容易地分离责任.通过将`switch`分成两部分,Single Responsibility成为两个不同的代码片段 (3认同)

anu*_*ith 9

对于偶然发现这个问题的人来说,这只是一个说明。

C# 8.0 引入了switch 表达式,这在这种情况下非常有用。

现在你可以做这样的事情:

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"),
};
Run Code Online (Sandbox Code Playgroud)


Rya*_*yan 8

C#7.3引入了元组相等性,这意味着你在问题中的初步想法几乎是正确的.您只需捕获您正在比较的值,如下所示:

var _test = ('A','B');
switch (_test)
{
   case var t when t == ('A', 'B'):
   Console.WriteLine("Case ok.");
   break;
}
Run Code Online (Sandbox Code Playgroud)

  • @SoftAllan您认为这可能是已接受的答案吗?这将是我的第一个,所以会很好:) (2认同)

Pan*_*vos 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.");
}
Run Code Online (Sandbox Code Playgroud)

您似乎正在尝试为解析器(?)创建状态匹配,以匹配特定模式.如果指定不同的状态类而不是对所有情况使用单个元组,则这可以与模式匹配一​​起使用.

您需要做的就是指定一个没有方法的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 ?");
}
Run Code Online (Sandbox Code Playgroud)

的a,d变量是强类型的,这意味着你不必添加任何的IState接口.它只是为了满足编译器.

通过使用结构而不是状态类型的类,您将获得与元组相同的内存优势.如果要使用解构,可以Deconstruct为每种类型添加方法,或Deconstruct在单独的静态类中使用扩展方法.


Sof*_*lan 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.");
            }

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以决定是否要按照我需要的顺序测试元组中的所有值。我认为性能上应该不会有太大差异。

如果还有另一种将元组与 switch 语句一起使用的方法,请随意举个例子。