2009-07-27 12 views
6

Ho una classe di raccolta con un metodo Equals che voglio passare in un metodo per fare il controllo di uguaglianza tra ogni elemento. Inoltre, voglio permettere il tipo delegato di operare su superclassi di T, così come T stessa:Posso specificare una relazione "supertipo" nei vincoli generici C#?

public delegate bool EqualityComparer<T>(T x, T y); 

public class Collection<T> 
{ 
    //... 

    public bool Equals<U>(Collection<T> other, EqualityComparer<U> eq) where T : U 
    { 
     // code using eq delegate to test equality between 
     // members of this and other collection 
    } 
} 

Purtroppo, il compilatore Borks su questo ('Collection.Equals()' non definisce parametro di tipo 'T '). C'è un modo per specificare questo tipo di vincolo/operazione?

risposta

4

No, temo che non sia possibile specificare un vincolo del genere. (Ho voluto che anche in occasione.)

È potrebbe scrivere un metodo generico statico con due parametri di tipo in una classe non generica però:

public delegate bool EqualityComparer<T>(T x, T y); 

public class Collection 
{ 
    public static Equals<T, U>(Collection<T> first, 
           Collection<T> second, 
           EqualityComparer<U> comparer) where T : U 
    { 

    } 
} 

e si potrebbe anche fare quella chiamata un metodo di istanza sulla classe generica, se vi piace:

// Implementing the static method: 
return first.Equals(second, new EqualityComparer<T>(comparer)); 

cui metodo di istanza della collezione sarebbe solo:

public bool Equals(Collection<T> other, EqualityComparer<T> eq) 
{ 
    // ... 
} 

Questo utilizza la controvarianza disponibile per creando delegati da C# 2 in poi.

+0

Ha senso essere possibile? Non rendere possibile l'inferenza è difficile perché le due dichiarazioni possono essere diffuse nell'intero progetto, o anche non essere specificate – Dykam

+1

Sì, ha perfettamente senso - è possibile specificare che un parametro di tipo deve essere una * sottoclasse * di qualcos'altro, quindi perché non il contrario? (Java permette questo, usando 'T super Foo' invece di' T estende Foo', btw.) –

+0

Ah - Vedo che stavi modificando la tua risposta per mettere quello che stavo scrivendo come un cavaliere :) – ShuggyCoUk

0

Se si desidera che il parametro type sia una classe ereditata di U, non è necessario utilizzare i generici, ma piuttosto utilizzare U come tipo formale dei parametri. E lì entra in gioco il polimorfismo! :)

1

Come Jon ha detto che non è possibile fare riferimento al T all'interno del vincolo in questo modo poiché è dichiarato a livello di classe.

Se è possibile scrivere il metodo senza accesso allo stato privato della collezione (o con il loro essere interno) allora si può riscrivere in questo modo:

public class Collection<T> 
{ 
    ... 
} 

public static class CollectionExtentions 
{ 
    public static bool Equals<T,U>(
      this Collection<T> first, 
      Collection<T> other, 
      EqualityComparer<U> eq) where T : U 
    { 
      ... // legal to use eq here on the T values with collections 
    } 
} 

Per inciso io suggerisco di usare Func<T,T,bool> piuttosto che il proprio delegato nominato

+0

Questo è in .NET 2, quindi Func non è disponibile :( – thecoop

+0

Ah - downer :(potresti aggiungere quel rider alla domanda (poiché ovviamente non potresti utilizzare la sintassi del metodo di estensione in modo da suggerire Jon il metodo statico utilizzato dal metodo di istanza è il migliore) – ShuggyCoUk

Problemi correlati