Linq Distinct方法异常:仅修改字符串后计数结果才正确
问题原因与解决方案
你的问题核心是字符串的默认相等比较没有忽略隐藏的不可见字符,导致肉眼看起来不同的LocationID被Distinct()错误判定为相等;而调用Reverse()后,这些隐藏字符的位置发生变化,反转后的字符串差异显现,Distinct()就能正确区分了。
具体分析
你看到的B A01A和A A01A看似只有首字符不同,但实际其中一个(或两个)字符串可能包含:
- 首尾的换行符、回车符、制表符(
\r/\n/\t) - 全角空格、不间断空格(
\u00A0)这类肉眼难辨的空白字符
默认的Distinct()使用字符串的严格值相等比较,会把这些隐藏字符纳入比较逻辑,错误地将两个字符串判定为相等,导致计数为1。而Reverse()会把这些隐藏字符移到字符串的另一端,使得反转后的字符串(比如A10A B\n和A10A A)差异明显,Distinct()就能正确识别出两个不同值。
解决方法
1. 先排查字符串实际内容
用代码输出带转义的字符串内容,定位隐藏字符:
foreach (var alloc in orderLine.OrderAllocations.Where(y => y.StatusID != 250)) { Console.WriteLine($"LocationID: '{alloc.LocationID}', 长度: {alloc.LocationID.Length}, 转义后: {System.Text.RegularExpressions.Regex.Escape(alloc.LocationID)}"); }
通过输出的转义字符(比如\r、\u00A0)确认问题根源。
2. 清理字符串后再去重
针对排查出的隐藏字符,先清理LocationID再调用Distinct():
var result = rootObject.OrderLines.Where(orderLine => orderLine.OrderAllocations .Where(y => y.StatusID != 250) .Select(y => // 去除首尾空白,替换特殊空白为普通半角空格,移除换行/回车 y.LocationID.Trim() .Replace("\u00A0", " ") .Replace("\t", " ") .Replace("\r", "") .Replace("\n", "") ) .Distinct() .Count() == 2); // 注意原代码中判断条件是==1,需根据实际需求修正
3. 自定义相等比较器(更灵活)
如果需要复用清理逻辑,可以自定义字符串比较器,传给Distinct():
public class LocationEqualityComparer : IEqualityComparer<string> { public bool Equals(string x, string y) { if (x == null && y == null) return true; if (x == null || y == null) return false; var cleanedX = CleanLocationString(x); var cleanedY = CleanLocationString(y); return cleanedX.Equals(cleanedY, StringComparison.Ordinal); } public int GetHashCode(string obj) { return obj == null ? 0 : CleanLocationString(obj).GetHashCode(); } private string CleanLocationString(string input) { return input.Trim() .Replace("\u00A0", " ") .Replace("\t", " ") .Replace("\r", "") .Replace("\n", ""); } } // 使用示例 var result = rootObject.OrderLines.Where(orderLine => orderLine.OrderAllocations .Where(y => y.StatusID != 250) .Select(y => y.LocationID) .Distinct(new LocationEqualityComparer()) .Count() == 2);
内容的提问来源于stack exchange,提问作者Nasim Ahmed
相关产品推荐
相关产品推荐

