2013-05-06 13 views
19

Supponiamo di avere un metodo generico non vincolato che funziona su tutti i tipi che supportano l'uguaglianza. Esso esegue controlli di uguaglianza coppie e così lavora a O (n):Come invocare condizionalmente un metodo generico con vincoli?

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

Ho anche un metodo generico vincolato che funziona solo con i tipi di supporto di smistamento. Si parte da ordinare l'elenco in O (n log n), e poi conta tutti i duplicati in un solo passaggio:

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

Quindi, un chiamante può scegliere di richiamare il metodo veloce se è staticamente noto che la il tipo di elementi della lista supporta l'ordinamento. Potrebbe accadere che il chiamante stesso funzioni con lo IList<T> generico dove T non è vincolato, quindi la sua unica opzione è di invocare il primo metodo (lento).

Ora, voglio il primo metodo per verificare in fase di runtime se il tipo T in realtà implementa l'interfaccia IComparable<T> e in caso affermativo, richiamare il metodo veloce:

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

Il problema è il compilatore rifiuta l'invocazione CountDuplicatesFast(list):

errore CS0314: il tipo 'T' non può essere utilizzato come parametro di tipo 'T' nel tipo generico o metodo 'Program.CountDuplicatesFast <T> (System.Collections.Generic.IList <T>) '. Non è prevista la conversione di boxe o la conversione del parametro di tipo da "T" a "System.IComparable <T>".

È possibile convincere il compilatore a fidarsi di me che so cosa sto facendo e di saltare il controllo dei vincoli?

+1

Hai provato a utilizzare Cast? 'return CountDuplicatesFast (list.Cast >(). ToList());' – Nevyn

+0

@Nevyn che produce "Il tipo 'System.IComparable ' non può essere utilizzato come parametro di tipo 'T' nel tipo generico o metodo 'UserQuery.MyType.CountDuplicatesFast (System.Collections.Generic.IList )'. Non esiste alcuna conversione implicita del riferimento da 'System.IComparable ' a 'System.IComparable >'. " –

+0

Interessante. Quasi la traccia giusta, ma completamente l'esecuzione sbagliata.Suppongo che avrei bisogno di scrivere un programma di test e provare alcune cose diverse. Penso che il cast potrebbe essere in grado di farlo ... ma non ho idea di come esattamente ... oh, beh, per una prima ipotesi non era troppo lontano :-) – Nevyn

risposta

7

Ecco un modo per farlo utilizzando dynamic:

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

o con la riflessione:

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 }); 
} 

Non credo che ci sia un modo per farlo in modo statico (cioè senza riflessione o dynamic).

+3

Bello! Non pensavo di lanciare l'argomento. Penso che il mio approccio sia necessario solo in rari casi in cui esiste un parametro di tipo ma nessun parametro di valore. –

+0

@VladimirReshetnikov hm..come sarebbe stato d'aiuto, anche allora? A meno che la classe helper non abbia aggiunto un parametro dummy per consentire di inferire il tipo, non lo vedo. –

8

È possibile utilizzare una classe di supporto e dynamic tipo di saltare i controlli a tempo di compilazione:

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 */ 
    } 
} 

Questo dovrebbe essere più veloce di pura riflessione a causa di meccanismi di caching DLR.

Problemi correlati