2013-05-06 107 views
19

假設我有一個不受約束的泛型方法,它支持所有支持相等的類型。它執行成對的相等性檢查等工作在爲O(n 2 如何有條件地調用帶約束的泛型方法?

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

我也有隻有具有類型支持排序工作的約束通用方法。它從Ø排序列表(N log n)的開始,然後計算所有副本中的一個通:

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

因此,主叫方可以選擇調用快速的方法,如果它是靜態已知的是,列表中的元素類型支持排序。可能發生的情況是,調用者自己使用通用的IList<T>,其中T是無約束的,所以它是調用第一個(慢)方法的唯一選項。

現在,我想第一種方法在運行時檢查類型T實際實現的接口IComparable<T>如果是這樣,調用快速的方法:

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' 不能在通用類型或方法被用作類型參數 'T'「Program.CountDuplicatesFast <Ť>(System.Collections.Generic.IList <Ť>)'。沒有從'T'到'System.IComparable <T>'的裝箱轉換或類型參數轉換。

是否有可能說服編譯器相信我,我知道我在做什麼,並跳過約束檢查?

+1

您是否嘗試過使用Cast? ()。ToList());' – Nevyn 2013-05-06 19:31:18

+0

@Nevyn產生的類型'System.IComparable '不能用作泛型類型中的類型參數'T',或者不能用作類型'System.IComparable ' 'UserQuery.MyType.CountDuplicatesFast (System.Collections.Generic.IList )'。沒有從'System.IComparable '到'System.IComparable >'的隱式引用轉換。「 – 2013-05-06 19:36:40

+0

有趣。幾乎是正確的軌道,但完全是錯誤的執行。我想我需要寫出一個測試程序並嘗試一些不同的東西。我認爲演員也許可以做到這一點......但我不知道該怎麼做......哦,第一次猜測它並不太遙遠:-) – Nevyn 2013-05-06 19:40:37

回答

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 )。

+3

不錯!我沒有想到要拋出這個論點。我認爲我的方法僅在極少數情況下需要,當存在類型參數但沒有值參數時。 – 2013-05-06 19:39:49

+0

@VladimirReshetnikov呃..即使那樣,這會有幫助嗎?除非輔助類添加了一個虛擬參數來讓這個類型被推斷,否則我不會看到它。 – 2013-05-06 21:57:37

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緩存機制純反射更快。