DEBUG.WIKI
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →

GeneratedRegex 写了 tr-TR,大小写匹配却仍按 en-US

两个 GeneratedRegex 仅文化名称不同,可能共用第一份实现;NonBacktracking 等回退路径又可能读取运行线程文化。区分两条路径,按预期文化验证 I、i、İ 和 ı。

为同一个忽略大小写的表达式声明了 en-US 和 tr-TR 两个 GeneratedRegex,结果两者都把大写 I 当成小写 i。交换声明顺序后结果还可能变化,应检查生成器是否把它们合并成了同一份实现。

.NET SDK 10.0.402、运行时 10.0.13 的具体故障中,只改变 cultureName,并不足以让这两个声明得到各自的匹配规则。土耳其语中的 i、İ 与 I、ı 分组不同,问题常在只测 ASCII 字母时被漏掉。

去重键没有包含文化名称

正式 .NET 10.0.13 的相关生成器按模式、选项和超时为实现去重,没有把文化名称放进这个键。后一个声明即使指定另一种文化,也可能返回第一份实现。查看生成源时,会看到两个入口指向同一个实例。

对能够真正生成匹配代码的简单模式,可以让模式文本不同、匹配语义相同,避免命中这条去重规则。下面是 C# partial 类型声明片段,两个入口保持各自的文化名称:

using System.Text.RegularExpressions;

static partial class Patterns
{
    [GeneratedRegex("^i$", RegexOptions.IgnoreCase, "en-US")]
    public static partial Regex English();

    [GeneratedRegex("^(?:i)$", RegexOptions.IgnoreCase, "tr-TR")]
    public static partial Regex Turkish();
}

非捕获组只为让这里的模式字符串不同,不应机械地改变带数字捕获组引用的复杂模式。修改后仍要检查生成源,确认两个入口不再指向同一实现,并验证实际字符集合。

回退到普通 Regex 是另一条路径

RegexOptions.NonBacktracking、过深的表达式树或某些忽略大小写的反向引用,可能让生成器无法产生专用匹配代码,转而发出普通 Regex 构造。这时会出现 SYSLIB1044 等有限支持提示,文化名称也可能没有传到最终构造中。

结果会取决于首次初始化时线程的 CurrentCulture。这种情况下,只把模式加一层非捕获组,不能解决文化丢失;它与前面的去重不是同一层问题。

若业务要求不随文化变化的规则,可以明确采用 RegexOptions.CultureInvariant,但这会改变原先指定 tr-TR 等文化的含义,不能用于悄悄替换文化敏感规则。确实需要指定文化且必须走普通 Regex 的入口,应在该文化下构造实例,保存并恢复调用线程原文化,让这个初始化过程保持同步、不跨 await;同时保留原选项和匹配超时。不要把应用全局文化改成 tr-TR 来迁就一个表达式。

固定字符样本,别只看文化标签

保留原生成源和构建版本,分别在 en-US、tr-TR 运行环境下验证 i、I、İ、ı,并覆盖声明顺序变化。真正按显式文化生成的实现,不应被运行时当前文化改变。

对会回退的模式,还要确认究竟使用了哪条初始化路径。XML 文档中写着某个文化,或属性中仍显示预期名称,都不能证明最终匹配器采用了它。

这类差异可能影响用户名、关键字和过滤规则。修改前保留原模式与预期结果,回查受影响输入;不要只把某次匹配改成通过,就顺手放宽整条校验或移除超时。