C#11中的List pattern

Intro

C# 11 将进一步支持增强模式匹配的使用,将会支持对于集合的模式匹配,我们可以结合 C# 8 引入的 Index && Range 来方便的进行集合的模式匹配.

Sample

我们直接来看示例吧:

第一组示例如下:

var array =
    //new List<int>()
    new[]
{
    1,2,3,4,5
}
;

if (array is [1, ..])
{
    Console.WriteLine("The first one is 1");
}
if (array is [.., 5])
{
    Console.WriteLine("The last one is 5");
}

看上面的代码大家应该可以看出来代码是什么意思的了,第一个匹配是集合第一个元素应该是 1,第二个匹配则是集合最后一个元素应该是 5,它等效于下面的代码:

bool flag = array != null && array.Length >= 1 && array[0] == 1;
if (flag)
{
    Console.WriteLine("The first one is 1");
}
bool flag2 = array != null && array.Length >= 1 && array[array.Length-1] == 1;
if (flag2)
{
    Console.WriteLine("The last one is 5");
}

这个示例有点简单,我们还可以从中间匹配和按范围做匹配,可以参考下面的示例:

if (array is [_, _, 3, ..])
{
    Console.WriteLine("The third one is 3");
}
if (array is [.., 4, _])
{
    Console.WriteLine("The second to last one is 4");
}

我们可以用 Range 操作符 .. 来表示有若干个元素,对于任意元素的匹配可以使用 _ 来匹配,上面代码等价于

bool flag3 = array != null && array.Length >= 3 && array[2] == 3;
if (flag3)
{
    Console.WriteLine("The third one is 3");
}
bool flag4 = array != null && array.Length >= 2 && array[array.Length-2] == 4;
if (flag4)
{
    Console.WriteLine("The second to last one is 4");
}

匹配序列示例:

if (array is [1, 2, 3, ..])
{
    Console.WriteLine("The sequence starts with 1,2,3");
}
if (array is [.., 3, 4, 5])
{
    Console.WriteLine("The sequence ends with 3,4,5");
}
if (array is [.., 3, 4, _])
{
    Console.WriteLine("The sequence ends with 3,4,_");
}

等价于:

bool flag5 = array != null && array.Length >= 3 && array[0] == 1 && array[1] == 2 && array[2] == 3;
if (flag5)
{
    Console.WriteLine("The sequence starts with 1,2,3");
}
bool flag6 = array != null && array.Length>=3 && array[num - 3] == 3 && array[num - 2] == 4 && array[num - 1] == 5;
if (flag6)
{
    Console.WriteLine("The sequence ends with 3,4,5");
}
bool flag7 = array != null && array.Length>=3 && array[num - 3] == 3 && array[num - 2] == 4;
if (flag7)
{
    Console.WriteLine("The sequence ends with 3,4,_");
}

除了直接元素值的匹配,我们还可以对元素进行类型匹配,可以参考下面这个示例:

var objects = new object[]
{
    1, "2", 3.0, "4", Guid.NewGuid()
};

if (objects is [1, "2", ..])
{
    Console.WriteLine($"{objects[0]},{objects[1]}");
}
if (objects is [.., string str, Guid guid])
{
    Console.WriteLine($"{str},{guid:N}");
}

这段代码等价于:

object[] objects = new object[]
   {
    1,
    "2",
    3.0,
    "4",
    Guid.NewGuid()
   };
if (objects != null && objects.Length >= 2 && objects[0] == 1 && objects[1] == "2")
{
    Console.WriteLine($"{objects[0]},{objects[1]}");
}
if (objects != null && objects.Length >= 2 && objects[objects.Length-2] is string str && objects[objects.Length-1] is Guid guid)
{
    Console.WriteLine($"{str},{guid:N}");
}

除了对于 if 的支持,对于 switch 的匹配也是支持的,示例如下:

var result = objects switch
{
        [1, ..] => 1,
        [_, "2", ..] => 2,
        [_, _, double val, ..] => (int)val,
        [.., ""] => 4,
        [.., string strVal, Guid _] => Convert.ToInt32(strVal),
        _ => -1
};
Console.WriteLine(result);

翻译为普通代码的话会是很长一段的 if ... else 这里就不贴了,感兴趣的可以反编译一下生成的 DLL

More

list pattern 的支持对于模式匹配来说是一个补充,也会让我们的代码写起来更加的简洁

目前匹配的时候 Range 操作符 .. 只能在一个模式中出现一次