导读:本期聚焦于小伙伴创作的《C#如何对List或数组进行模式匹配?掌握列表模式匹配方法的实用技巧》,敬请观看详情。在C# 11之前,要判断一个集合的前几个元素是否符合预期,往往得手写循环或借助LINQ写一堆条件。列表模式(List Patterns)把这种判断直接下沉到模式语法里,可以用方括号加下划线、var声明和切片模式一次性描述集合结构。比如用[1, 2, _]匹配长度为三且前两位是1和2的数组,用[.. var rest]把剩余元素捕获到变量。本文从语法构成、匹配规则、与switch表达式结合,以及常见误用四个角度拆解,帮你把繁琐的集合校验改写成可读性更高的声明式代码,不再依赖额外的辅助方法。

在C# 11引入列表模式(List Patterns)之后,针对数组或List等线性集合的结构化匹配变得非常直接。过去我们常常需要写循环、用索引访问或者借助LINQ的Take、Skip来判断集合头部或整体形态,而现在可以在is表达式或switch中直接用方括号语法描述期望的集合内容。这种能力不仅减少了临时变量,也让业务规则一眼可读。

C#如何对List或数组进行模式匹配?掌握列表模式匹配方法的实用技巧

一、列表模式的基本语法

列表模式使用方括号[]来包裹一系列子模式,每个子模式对应集合中从索引0开始的一个元素。最常见的形式包括常量模式、var模式和丢弃模式(下划线)。当被匹配的对象实现了Length或Count以及索引器(如数组、List<T>、Span<T>等),编译器就能生成对应的元素检查逻辑。

例如,我们需要判断一个int数组是不是长度为3,并且第一个元素是1、第二个是2、第三个可以是任意值。传统写法要写多个条件,而列表模式只需一行。下面给出基础示例,注意代码块内所有尖括号都已转义:

int[] numbers = { 1, 2, 99 };

// 列表模式匹配:长度为3,前两个固定,第三个忽略
if (numbers is [1, 2, _])
{
    Console.WriteLine("匹配成功:前两位是1和2");
}

// 使用 var 捕获具体值
if (numbers is [1, 2, var third])
{
    Console.WriteLine($"第三位是:{third}");
}

上面的_是丢弃模式,表示“这里有任何值都行,但不关心具体内容”。而var third则会把实际元素绑定到变量third上,方便后续使用。这两种写法在编译后都会先检查数组长度是否为3,再依次比对或取值。

二、切片模式捕获剩余元素

如果集合长度不固定,但我们只关心头尾或中间某段,就可以用切片模式..。切片模式代表“零个或多个剩余元素”,并且可以配合var把剩余部分捕获成一个新的集合。切片在列表中只能出现一次,且必须能明确推导出前后元素的位置关系。

假设我们处理用户输入的命令参数,第一个是命令名,后面可能跟任意数量的参数。用切片模式可以轻松拆解:

string[] args = { "run", "--verbose", "file.txt" };

if (args is ["run", .. var rest])
{
    Console.WriteLine($"执行run命令,参数个数:{rest.Length}");
    foreach (var item in rest)
    {
        Console.WriteLine(item);
    }
}

这里.. var rest把索引1及以后的所有元素捕获为string数组rest。如果args长度仅为1,rest也会是一个空数组,匹配依然成立。切片模式极大简化了“前缀匹配”类需求,不需要手动写Skip(1)。

三、与switch表达式结合做分支分发

列表模式真正发挥威力是在switch表达式中。我们可以根据集合的形状返回不同结果,而不必写层层if-else。下面的例子对一个坐标点数组做分类:

int[] point = { 0, 0 };

string description = point switch
{
    [0, 0] => "原点",
    [0, var y] => $"Y轴上的点,y={y}",
    [var x, 0] => $"X轴上的点,x={x}",
    [var x, var y] => $"普通点({x},{y})",
    [] => "空集合",
    _ => "更多维度的数据"
};

Console.WriteLine(description);

这段代码中,编译器会按照从上到下的顺序尝试匹配。空数组匹配[],二维点根据坐标值落入不同分支。需要注意,如果写_作为兜底,它必须放在最后,否则后面的分支永远无法到达。列表模式与switch结合后,原本需要多个Length判断和索引访问的逻辑被压缩成了一张“形状对照表”。

对于List<T>对象,上述写法完全通用,因为List也提供了Count和索引器。不过若集合是IEnumerable但没有实现索引器,则无法使用列表模式,这是编译期就会报错的限制。

四、常见误用与性能注意点

一个常见误区是认为列表模式会自动递归匹配嵌套集合。实际上[1, [2, 3]]这种写法在C#中并不表示“第二个元素是一个数组[2,3]”,除非内层本来就是模式(如对元素本身再用列表模式需写成[1, var x] where x is [2,3]或用属性模式)。直接嵌套方括号只适用于元素自身支持列表模式的情形,比如元素是数组时可以用[ [1,2], _ ]匹配外层数组的第一个元素是长度为2且值为1、2的数组。

另一个注意点是切片模式不能出现多次,也不能和“长度不固定但又要求尾部固定”之外的复杂约束混用。如果业务需要“中间某段忽略、头尾都固定”,可以写成[head, .., tail],但无法表达“忽略前两个之后的第三个必须是5”这类跳跃约束,那种情况仍需配合where子句或先切片再判断。

int[] data = { 1, 9, 9, 5 };

// 正确:头为1,尾为5,中间任意
if (data is [1, .., 5])
{
    Console.WriteLine("首尾符合");
}

// 错误示例(编译不过):试图跳过中间直接指定末尾前一位
// if (data is [1, .., 9, 5]) { }

性能方面,列表模式的匹配在编译期会展开为对Length和索引器的顺序访问,时间复杂度是O(n)且不会创建多余的枚举器。相比先用ToList再LINQ链式处理,它在分配上更友好。但在极热路径中,若集合很大且只关心头部几个元素,显式写索引判断可能比完整切片捕获更省内存,因为切片捕获会生成新数组。

五、小结与实践建议

列表模式让C#在集合处理上补齐了“结构性匹配”的短板。日常开发中,凡是出现“判断数组前几个元素”“按集合形状分支”“拆解命令参数”的逻辑,都可以优先用is [ ... ]或switch中的列表模式改写。它带来的不只是代码变短,更是意图表达的清晰化。

建议在团队代码规范中,将传统的arr.Length == x && arr[0] == y类判断逐步替换为列表模式,并对新人说明切片模式只能出现一次、被匹配对象需支持索引器等约束,避免误用导致编译错误。熟练掌握后,你会发现很多原本啰嗦的集合校验都能收敛成几行声明式代码。

C#列表模式匹配List数组匹配修改时间:2026-08-07 19:03:42

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。