前言
下面这段代码,你能发现什么问题吗?
List<int> a = new List<int>{ 1, 2, 3, 4, 5 };var last2 = a.TakeLast(2);foreach (var item in last2)
{ Console.WriteLine(item);
}a.AddRange(new[] { 11, 12, 13, 14, 15 });foreach (var item in last2)
{Console.WriteLine(item);
}
你能说出2次Console.WriteLine
的输出结果吗?
是4 5 4 5
还是4 5 14 15
?
其实答案是:
你没看错,第2次会出现3个元素。
原因
这并不是我们的代码有问题,而是System.Linq的Bug。
我们直接调试了dotnet/runtime源代码,发现调用TakeLast
时生成了ListPartition对象:
关键是_maxIndexInclusive
赋值是5(Count),按我的理解不应该是4(Index)吗!?
这就导致了下面的问题,当我们增加了元素后,它是这样计算的:
private int Count
{get{int count = _source.Count;if (count <= _minIndexInclusive){return 0;}return Math.Min(count - 1, _maxIndexInclusive) - _minIndexInclusive + 1;}
}
在这里,_maxIndexInclusive又是按Index理解的:
Math.Min(count - 1, _maxIndexInclusive) - _minIndexInclusive + 1
如果还是将_maxIndexInclusive按Count理解,就是正确的了:
Math.Min(count, _maxIndexInclusive) - _minIndexInclusive
结论
我怀疑,这是由于2个程序员分别实现的功能,导致对变量名含义理解不一致造成的BUG。
这也说明,起个意义明确的变量名是多么重要啊!