由于整数溢出,OrderByDescending 操作不正确
OrderByDescending operates incorrectly due to integer overflow
在浏览 Enumerable class 的 .Net Framework 源代码时,我发现用于排序的内部 EnumerableSorter class CompareKeys method 有以下一行:
return descending ? -c : c;
其中 c
是 IComparer.Compare Method (T, T) 方法调用的结果,实际上并不强制我们只使用 -1、1 或 0 来表示排序。
考虑到-Int32.MinValue == Int32.MinValue
由于整数溢出,会导致排序不正确,可以通过以下代码片段证明:
public class Value : IComparable<Value>
{
private readonly Int32 _value;
public Value(Int32 value)
{
this._value = value;
}
public Int32 CompareTo(Value other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
var cmp = this._value.CompareTo(other._value);
if (cmp > 0)
return Int32.MaxValue;
if (cmp < 0)
return Int32.MinValue;
return 0;
}
public override String ToString()
{
return this._value.ToString();
}
}
private static void Print<T>(String header, IEnumerable<T> values)
{
Console.WriteLine(header);
foreach (var item in values)
{
Console.WriteLine(item);
}
Console.WriteLine();
}
public static void Main()
{
try
{
var notSorted = new[] { 1, 3, 2 }
.Select(i =>
new Value(i))
.ToArray();
Print("Not sorted", notSorted);
Print("Sorted by", notSorted.OrderBy(item => item));
Print("Sorted by descending", notSorted.OrderByDescending(item => item));
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
对于 OrderByDescending
它产生:
Sorted by descending
3
1
2
这是预料之中的,但也是一个明显不正确的结果。
所以它似乎是 .Net 中的一个缺陷,但如果 CompareTo
以合理的方式实现,则不太可能发生。我说得对吗?
更新:
正如SLaks the issue has been known for a long time but hasn't been fixed despite all the new releases - https://connect.microsoft.com/VisualStudio/feedback/details/634949/orderbydescending-fails-in-linq-to-objects-when-a-comparer-returns-int-minvalue
指出的那样
正如usr .Net Core has this issue fixed - https://github.com/dotnet/corefx/blob/35e03c78d89d02f2d3b4a1f8b277a35c88f45750/src/System.Linq/src/System/Linq/OrderedEnumerable.cs#L628
指出的那样
似乎没有太多的答案,所以:
正如 SLaks 指出的那样,这个问题早已为人所知,但 从未在 .NET Framework 中得到修复 尽管有所有新版本(.Net 4.6.1 作为现在)- https://connect.microsoft.com/VisualStudio/feedback/details/634949/orderbydescending-fails-in-linq-to-objects-when-a-comparer-returns-int-minvalue.
避免此问题的唯一方法是不 return 来自 CompareTo
实现的 Int32.MinValue
。
但正如 usr 指出的那样。Net Core 已修复此问题 - https://github.com/dotnet/corefx/blob/35e03c78d89d02f2d3b4a1f8b277a35c88f45750/src/System.Linq/src/System/Linq/OrderedEnumerable.cs#L628
在浏览 Enumerable class 的 .Net Framework 源代码时,我发现用于排序的内部 EnumerableSorter class CompareKeys method 有以下一行:
return descending ? -c : c;
其中 c
是 IComparer.Compare Method (T, T) 方法调用的结果,实际上并不强制我们只使用 -1、1 或 0 来表示排序。
考虑到-Int32.MinValue == Int32.MinValue
由于整数溢出,会导致排序不正确,可以通过以下代码片段证明:
public class Value : IComparable<Value>
{
private readonly Int32 _value;
public Value(Int32 value)
{
this._value = value;
}
public Int32 CompareTo(Value other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
var cmp = this._value.CompareTo(other._value);
if (cmp > 0)
return Int32.MaxValue;
if (cmp < 0)
return Int32.MinValue;
return 0;
}
public override String ToString()
{
return this._value.ToString();
}
}
private static void Print<T>(String header, IEnumerable<T> values)
{
Console.WriteLine(header);
foreach (var item in values)
{
Console.WriteLine(item);
}
Console.WriteLine();
}
public static void Main()
{
try
{
var notSorted = new[] { 1, 3, 2 }
.Select(i =>
new Value(i))
.ToArray();
Print("Not sorted", notSorted);
Print("Sorted by", notSorted.OrderBy(item => item));
Print("Sorted by descending", notSorted.OrderByDescending(item => item));
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
对于 OrderByDescending
它产生:
Sorted by descending 3 1 2
这是预料之中的,但也是一个明显不正确的结果。
所以它似乎是 .Net 中的一个缺陷,但如果 CompareTo
以合理的方式实现,则不太可能发生。我说得对吗?
更新:
正如SLaks the issue has been known for a long time but hasn't been fixed despite all the new releases - https://connect.microsoft.com/VisualStudio/feedback/details/634949/orderbydescending-fails-in-linq-to-objects-when-a-comparer-returns-int-minvalue
指出的那样正如usr .Net Core has this issue fixed - https://github.com/dotnet/corefx/blob/35e03c78d89d02f2d3b4a1f8b277a35c88f45750/src/System.Linq/src/System/Linq/OrderedEnumerable.cs#L628
指出的那样似乎没有太多的答案,所以:
正如 SLaks 指出的那样,这个问题早已为人所知,但 从未在 .NET Framework 中得到修复 尽管有所有新版本(.Net 4.6.1 作为现在)- https://connect.microsoft.com/VisualStudio/feedback/details/634949/orderbydescending-fails-in-linq-to-objects-when-a-comparer-returns-int-minvalue.
避免此问题的唯一方法是不 return 来自 CompareTo
实现的 Int32.MinValue
。
但正如 usr 指出的那样。Net Core 已修复此问题 - https://github.com/dotnet/corefx/blob/35e03c78d89d02f2d3b4a1f8b277a35c88f45750/src/System.Linq/src/System/Linq/OrderedEnumerable.cs#L628