2009-07-27 14 views
6

Tengo una clase de colección con un método Equals que quiero pasar en un método para hacer la comprobación de igualdad entre cada elemento. Por otra parte, quiero permitir que el tipo de delegado para operar en superclases de T, así como T en sí:¿Puedo especificar una relación de "supertipo" en las restricciones genéricas de 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 
    } 
} 

Por desgracia, el compilador Borks sobre esto ('Collection.Equals()' no define parámetro de tipo 'T '). ¿Hay alguna forma de especificar este tipo de restricción/operación?

Respuesta

4

No, me temo que no puede especificar una restricción como esa. (He querido también en alguna ocasión.)

Usted podría escribir un método genérico estático con dos parámetros de tipo en una clase no genérica sin embargo:

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 
    { 

    } 
} 

y que incluso podría hacer esa llamada un método de instancia de la clase genérica si quieres:

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

donde método de instancia de la colección sólo sería:

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

Esto usa la contravariancia disponible para creando delegados desde C# 2 en adelante.

+0

¿Tiene sentido que sea posible? No sería posible hacerlo por inferencia porque las dos declaraciones pueden distribuirse en todo el proyecto, o incluso no especificarse – Dykam

+1

Sí, tiene perfecto sentido: puede especificar que un parámetro de tipo debe ser una * subclase * de otra cosa, ¿por qué no al revés? (Java lo permite, usando 'T super Foo' en lugar de' T extiende Foo', por cierto.) –

+0

Ah - Veo que estabas editando tu respuesta para poner lo que estaba escribiendo como piloto :) – ShuggyCoUk

0

Si desea explícitamente que el parámetro de tipo sea una clase heredada de U, no necesita genéricos, sino que usa U como el tipo formal de los parámetros. ¡Y ahí entra el polimorfismo! :)

1

Como dijo Jon, no se puede hacer referencia a la T dentro de la restricción de esa manera ya que se declara en el nivel de clase.

Si usted puede escribir el método sin acceso al estado privada de la colección (o con su ser interno) entonces se puede reescribir así:

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

Por cierto le sugiero que utilice Func<T,T,bool> en lugar de su propio delegado designado

+0

Esto está en .NET 2, por lo que Func no está disponible :( – thecoop

+0

Ah - downer :(es posible que desee agregar ese jinete a la pregunta (ya que obviamente no sería capaz de utilizar la sintaxis del método de extensión para la sugerencia de Jon) del método estático utilizado por el método de instancia es el mejor) – ShuggyCoUk

Cuestiones relacionadas