Type参数的约束(类型设置为IComparable但无法将Type作为参数发送到采用IComparable的方法)

div*_*ero 0 c# generics constraints

这是我的代码

public class UnOrderedMaxPQ<Key> where Key : IComparable
 {
    private Key[] array;
    private int N;
    public UnOrderedMaxPQ(int size)
      {
        array = new Key[size];
       }
         .......
     public Key DelMax()
      {  
InsertionSort.Sort(array);//Error cannot convert from 'Key[]' to IComparable[]' 

   return array[--N];
   }
  }
Run Code Online (Sandbox Code Playgroud)

这是插入排序代码

 public class InsertionSort
 {
     public static void Sort(IComparable[] array)
    {
        for (int i = 0; i < array.Length-1; i++)
        {
            for (int j = i + 1; j <=0;j--)
            {
                if (Less(array[j], array[i]))
                {
                    Exchange(array, j, i);
                }
                else
                    break;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

所以我的问题是当我将"Key"限制为IComparable时,为什么我不能将它传递给以IComparable作为参数的Sort方法?我需要做些什么改变才能让它发挥作用?

dbc*_*dbc 5

您正在尝试利用数组协方差 - 当需要基类型数组时,您可以替换派生类型的数组.但是,在c#中,这仅适用于引用类型.来自文档:

对于任何两个引用类型A和B,如果从A到B存在隐式引用转换(第6.1.4节)或显式引用转换(第6.2.3节),则数组类型A中也存在相同的引用转换[ R]到数组类型B [R],其中R是任何给定的秩指定符(但对于两种数组类型都相同).这种关系称为阵列协方差.数组协方差特别意味着数组类型A [R]的值实际上可以是对数组类型B [R]的实例的引用,前提是存在从B到A的隐式引用转换.

因此,如果将类型约束Key为类(引用类型),则代码将编译:

public class UnOrderedMaxPQ<Key> where Key : class, IComparable
{
}
Run Code Online (Sandbox Code Playgroud)

当然,这意味着你将无法使用值类型,如int一个Key,所以你可能不希望这样做.

或者,您可以通过声明您的Sort方法(以及方法ExchangeLess)也是通用的来消除对数组协方差的需要:

public class InsertionSort
{
    public static void Sort<TComparable>(TComparable[] array) where TComparable : IComparable
    {
    }

    private static void Exchange<TComparable>(TComparable[] array, int j, int i) where TComparable : IComparable
    {
    }

    private static bool Less<TComparable>(TComparable iComparable, TComparable iComparable_2) where TComparable : IComparable
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您的代码将编译并适用于引用和值类型.此解决方案还避免了引用类型的装箱,如下所述:泛型的优点(C#编程指南).

更新

数组协方差不适用于值类型但泛型方法有效的一个原因是值类型可以具有不同的大小,但引用类型引用都具有相同的大小.所有引用类型变量TComparable foo在堆栈上占用4(32位)或8(64位)字节,无论具体类型如何TComparable(当然,它们所引用的托管内存可以占用任何大小).但相当类型,比如说DateTime,可以在堆栈比其他可比的价值类型,例如在不同的尺寸Int16.因此,为一个生成的MSIL不需要为另一个生效.

中被描述为泛型很好地处理这种差异在这里:

运行时的泛型(C#编程指南)

将泛型类型或方法编译为Microsoft中间语言(MSIL)时,它包含将其标识为具有类型参数的元数据.如何使用泛型类型的MSIL根据提供的类型参数是值类型还是引用类型而不同.

当首次使用值类型作为参数构造泛型类型时,运行时会创建一个专用泛型类型,其中提供的参数或参数将替换为MSIL中的相应位置.为每个用作参数的唯一值类型创建一次专用泛型类型.

...

泛型对参考类型的工作方式有所不同.第一次使用任何引用类型构造泛型类型时,运行时会创建一个专用泛型类型,其中对象引用将替换MSIL中的参数.然后,每次构造类型以引用类型作为其参数进行实例化时,无论它是什么类型,运行时都会重用先前创建的泛型类型的专用版本.这是可能的,因为所有引用都是相同的大小.

顺便说一句,而不是限制Key来实现IComparable,你可能要限制它来实现IComparable<Key>.这可以保证比较中的类型安全性,也可以避免对值类型进行装箱.