如何有条件地调用带约束的泛型方法?

时间:2013-05-06 19:21:36

标签: c# .net generics clr constraints

假设我有一个无约束的泛型方法,适用于支持相等的所有类型。它执行成对等式检查,因此适用于 O(n 2

public static int CountDuplicates<T>(IList<T> list) 
{
    /* ... */ 
}

我还有一个约束泛型方法,只适用于支持排序的类型。它从 O(n log n)中的列表排序开始,然后在一次通过中计算所有重复项:

public static int CountDuplicatesFast<T>(IList<T> list) 
    where T : IComparable<T> 
{
    /* ... */ 
}

因此,如果静态地知道列表的元素类型支持排序,则调用者可以选择调用fast方法。可能会发生调用者本身使用泛型IList<T>,其中T是不受约束的,因此它是调用第一个(慢)方法的唯一选项。

现在,我希望第一个方法在运行时检查类型T是否实际实现了接口IComparable<T>,如果是,请调用fast方法:

public static int CountDuplicates<T>(IList<T> list)
{
    if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
    {
        return CountDuplicatesFast(list);
    }
    else
    {
        /* use the slow algorithm */
    }
}

问题是编译器拒绝调用CountDuplicatesFast(list)

错误CS0314:类型'T'不能用作泛型类型或方法'Program.CountDuplicatesFast&lt; T&gt;(System.Collections.Generic.IList&lt; T&gt;)'中的类型参数'T'。从'T'到'System.IComparable&lt; T&gt;'没有装箱转换或类型参数转换。

是否有可能说服编译器相信我,我知道自己在做什么,并跳过约束检查?

2 个答案:

答案 0 :(得分:8)

您可以使用辅助类和dynamic类型来跳过编译时检查:

sealed class CountDuplicatesFastCaller
{
    public int Call<T>(IList<T> list) where T : IComparable<T>
    {
        return CountDuplicatesFast(list);
    }
}

public static int CountDuplicates<T>(IList<T> list)
{
    if (typeof (IComparable<T>).IsAssignableFrom(typeof (T)))
    {
        return ((dynamic) new CountDuplicatesFastCaller()).Call(list);
    }
    else
    {
        /* use the slow algorithm */
    }
}

由于DLR缓存机制,这应该比纯反射更快。

答案 1 :(得分:7)

以下是使用dynamic

的方法
if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
    return CountDuplicatesFast((dynamic)list);
}

或反思:

if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
    var method = typeof(MyType).GetMethod("CountDuplicatesFast");
    var generic = method.MakeGenericMethod(typeof(T));
    return (int)generic.Invoke(null, new object[] { list });
}

我认为没有办法静态地做到这一点(即没有反思或dynamic)。