2013-05-06 21 views
19

Załóżmy, że mam nieograniczoną ogólną metodę, która działa na wszystkich typach wspierających równość. Wykonuje parami kontroli równości a więc pracuje w O (n):Jak warunkowo wywołać ogólną metodę z ograniczeniami?

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

również mieć ograniczony sposób ogólny, który pracuje tylko z rodzaju wsparcie sortowania. Zaczyna się od sortowania listy w O (n log n), a następnie zlicza wszystkie duplikaty w jednym przebiegu:

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

Tak, dzwoniący może wybrać, aby wywołać szybką metodę, jeśli jest on statycznie wiadomo, że typ elementów listy obsługuje zamawianie. Może się zdarzyć, że wywołujący działa z generycznym IList<T>, gdzie T jest nieograniczony, więc jest jedyną opcją wywołania pierwszej (powolnej) metody.

Teraz chcę pierwsza metoda, by sprawdzić w czasie wykonywania, jeśli typ T faktycznie implementuje interfejs IComparable<T> a jeśli tak, to wywołać szybką metodę:

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

Problemem jest kompilator odrzuca wezwanie CountDuplicatesFast(list):

błąd CS0314: typu „T” nie może być stosowana jako parametr typu „T” w ogólnym typie lub metodą „Program.CountDuplicatesFast <T> (System.Collections.Generic.IList <T>) ". Nie ma konwersji boksu lub konwersji parametrów z "T" na "System.IComparable <T>".

Czy można przekonać kompilator, aby zaufał mi, że wiem, co robię, i pominąć kontrolę ograniczenia?

+1

Czy próbowałeś korzystać z Cast? 'powrotu CountDuplicatesFast (list.Cast >() ToList()).;' – Nevyn

+0

@Nevyn który wytwarza „typu 'System.IComparable ' nie może być stosowana jako parametr typu 'T' lub typu rodzajowego metoda "UserQuery.MyType.CountDuplicatesFast (System.Collections.Generic.IList )." Nie istnieje żadna niejawna konwersja odniesienia z "System.IComparable " na "System.IComparable >". " –

+0

Interesujące. Prawie właściwy tor, ale całkowicie złe wykonanie.Przypuszczam, że musiałbym napisać program testowy i spróbować kilku różnych rzeczy. Myślę, że obsada może być w stanie to zrobić ... ale nie mam pojęcia, jak dokładnie ... no cóż, na pierwszy rzut oka to nie było zbyt daleko :-) – Nevyn

Odpowiedz

7

Oto sposób to zrobić przy użyciu dynamic:

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

Albo z refleksji:

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

Nie sądzę, że istnieje sposób, aby to zrobić statycznie (czyli bez odbicia lub dynamic).

+3

Miło! Nie myślałem o rzuceniu kłótni. Myślę, że moje podejście jest potrzebne tylko w rzadkich przypadkach, gdy istnieje parametr typu, ale nie ma parametrów wartości. –

+0

@VladimirReshetnikov hm ... jak by to pomogło, nawet wtedy? O ile klasa pomocnika nie doda fikcyjnego parametru, aby typ mógł zostać wywnioskowany, nie widzę go. –

8

Można użyć klasy pomocnika i dynamic typ pominąć kontrole kompilacji:

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

To powinno być szybsze niż czystej refleksji ponieważ mechanizmów buforowania DLR.

Powiązane problemy