我觉得奇怪的是C#让我在我的类上调用sort而没有指定一种方法来对它们进行排序,也没有写出比较重载。当我运行此代码时,会弹出此错误
List<MyClass> myClassArray= new List<MyClass>();
//myClassArray.add(...);
myClassArray.Sort();
An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: Failed to compare two elements in the array.
为什么C#让我在不知道如何排序时编译这段代码! -edit -
Codex问为什么会这样做。我在评论中写了一个关于它为什么会这样做的理论。这里有一些示例代码。
class A : IComparable<A>
{
public int CompareTo(A a) { return 0; }
}
class C //: IComparable<A>
{
public int CompareTo(A a) { return 0; }
}
static void test()
{
A a = new A();
bool b;
C c = new C();
object o = a;
IComparable<A> ia = (IComparable<A>)o;
b = ia == ia;
o = c;
IComparable<A> ic = (IComparable<A>)o;
b = ic == ic;
//uncomment this to get a compile time error
//IComparable<A> ic2 = c;
return;
}
如果在返回之前取消注释该行,则会出现编译时错误。当您在类c中取消注释IComparable时,它将编译并运行。
答案 0 :(得分:13)
对List&lt; T&gt;的通用参数没有约束。要求它实现IComparable&lt; T&gt;。如果有,它会(某种程度上)保证元素可以被排序,但是你将无法使用List&lt; T&gt;保留任何未实现IComparable的内容。由于您可能不会对您创建的每个列表进行排序,因此这是正确的决定。
答案 1 :(得分:2)
排序应检查您的对象是否实现IComparable。这是一个运行时检查,因为你可能没有实现它,默认的比较器不知道如何处理你的对象,所以它抛出异常。
它允许编译,因为这不是语言功能,它是一个框架功能。
答案 2 :(得分:2)
仅供参考;基本上,它使用Comparer<T>.Default
进行比较。这实现了IComparer<T>
,可以比较T
类型的2个对象。实际实施是在您第一次要求时选择的(按T
);框架使用许多模式来选择实现 - 例如,类,“常规”结构和Nullable<T>
都是单独处理的。同样,它根据T
是否实现IComparable<T>
,IComparable
或两者都没有做出选择(在这种情况下它会抛出异常)。
这提供了一种非常简单的方法来进行“鸭子类型”排序。同样,还有EqualityComparer<T>.Default
检查IEquatable<T>
,否则默认为object.Equals
。
答案 3 :(得分:0)
C#可能只是在System.Object中有一些概念,关于你如何订购对象,就像它使用Equals
来比较它们的身份一样。
不幸的是,这导致了对内涵性与扩展性,本地化等的担忧。
有一个IComparable<T>
接口,但内置值类型无法实现这样的接口。
因此,没有好的方法可以在编译时查看类型,并明确知道它是否具有有意义的排序。 =(
在C#中演变的机制是使用IComparer<T>
返回的Comparer<T>.Default
实例,并在尝试对缺少排序的内容进行排序时收到运行时错误。
通过允许多个IComparer
和IComparer<T>
s,您可以拥有多个替代排序的概念,这些排序可以在同一类型上运行,所以这很好,但一切都不顺利。
在内部,c#使用一系列规则来查找Comparer<T>.Default
,如果T是IComparable<T>
的实例或Nullable<T>
的形式,则它会以不同的方式处理。
e.g。来自system/collections/generic/comparer.cs的代码:
public static Comparer<T> Default {
get {
Comparer<T> comparer = defaultComparer;
if (comparer == null) {
comparer = CreateComparer();
defaultComparer = comparer;
}
return comparer;
}
}
private static Comparer<T> CreateComparer() {
Type t = typeof(T);
// If T implements IComparable<T> return a GenericComparer<T>
if (typeof(IComparable<T>).IsAssignableFrom(t)) {
//return (Comparer<T>)Activator.CreateInstance(typeof(GenericComparer<>).MakeGenericType(t));
return (Comparer<T>)(typeof(GenericComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(t));
}
// If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
Type u = t.GetGenericArguments()[0];
if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
//return (Comparer<T>)Activator.CreateInstance(typeof(NullableComparer<>).MakeGenericType(u));
return (Comparer<T>)(typeof(NullableComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(u));
}
}
// Otherwise return an ObjectComparer<T>
return new ObjectComparer<T>();
}
从本质上讲,这可以让Microsoft逐步为自己的代码添加特殊情况,但遗憾的是,仅使用Comparer<T>.Default
进行自定义IComparable<T>
实例的代码非常糟糕。
IList<T>
上的Sort方法假定Comparer<T>.Default
会提供可用于比较对象的内容,但它无法查看类型T并告诉它,所以它假设它是安全的,并且只在运行时稍后才意识到MyClass
没有在播放。