2008-09-23 21 views
5

Estoy tratando de comprender el concepto de .NET Generics y utilizarlos en mi propio código, pero sigo teniendo un problema..NET Método genérico Pregunta

¿Alguien puede tratar de explicarme por qué no se compila la siguiente configuración?

public class ClassA 
{ 
    ClassB b = new ClassB(); 

    public void MethodA<T>(IRepo<T> repo) where T : ITypeEntity 
    { 
     b.MethodB(repo); 
    } 
} 

public class ClassB 
{ 
    IRepo<ITypeEntity> repo; 

    public void MethodB(IRepo<ITypeEntity> repo) 
    { 
     this.repo = repo; 
    } 
} 

me sale el siguiente error:
no se puede convertir de IRepo < 'T> a IRepo <' ITypeEntity>

MethodA se llama con un IRepo < 'DetailType> parámetro de objeto, donde DetailType hereda de ITypeEntity .

Sigo pensando que esto debería compilar ya que estoy restringiendo T en MethodA para que sea del tipo ITypeEntity.

Cualquier idea o comentario sería extremadamente útil.

Gracias.

Edit: Nick R tiene una gran sugerencia, pero desafortunadamente en mi contexto, no tengo la opción de hacer ClassA Genérico. ClassB podría ser sin embargo.

Respuesta

3

La herencia no funciona igual cuando se usan genéricos. Como señala Smashery, incluso si TypeA hereda de TypeB, myType < TypeA> no hereda de myType < TypeB>.

Como tal, no se puede hacer una llamada a un método definido como MethodA (myType < TypeB> b) esperando un TypeB myType <> y darle un TypeA myType <> lugar. Los tipos en cuestión tienen que coincidir exactamente. Por lo tanto, la siguiente no se compilará:

myType<TypeA> a; // This should be a myType<TypeB>, even if it contains only TypeA's 

public void MethodB(myType<TypeB> b){ /* do stuff */ } 

public void Main() 
{ 
    MethodB(a); 
} 

Así, en su caso, lo que tendría que pasar en un IRepo < ITypeEntity> a MethodB, incluso si sólo contiene DetailTypes. Necesitarías hacer alguna conversión entre los dos. Si estaba usando un IList genérico, podría hacer lo siguiente:

public void MethodA<T>(IList<T> list) where T : ITypeEntity 
{ 
    IList<T> myIList = new List<T>(); 

    foreach(T item in list) 
    { 
    myIList.Add(item); 
    } 

    b.MethodB(myIList); 
} 

Espero que esto sea útil.

2

El problema es difícil de entender. DetailType puede heredar de ITypeEntity, pero en realidad no es ITypeEntity. Su implementación de DetailType podría presentar diferentes funcionalidades, por lo que DetailType implementa ITypeEntity pero no es igual a ITypeEntity. Espero que tenga sentido ...

0

Si B es una subclase de A, eso no significa que Class<B> es una subclase de Class<A>. Por lo tanto, por esta misma razón, si dice "T es ITypeEntity", eso no significa que "IRepo<T> es un IRepo<ITypeEntity>". Puede que tenga que escribir su propio método de conversión si quiere que esto funcione.

0

T es una variable de tipo que estará vinculada a un tipo de tipo parcial en el uso. La restricción garantiza que ese tipo representará un subconjunto de los tipos que implementan ITypeEntity, excluyendo otros tipos que implementan la interfaz.

0

en tiempo de compilación, aunque lo está restringiendo, el compilador solo sabe que T en MethodA es un tipo de referencia. no sabe a qué tipo está limitado.

3

Bueno, esto compila bien. Básicamente rediseñé las clases para tomar los parámetros genéricos. Esto puede estar bien en tu contexto.

public interface IRepo<TRepo> 
{ 
} 

public interface ITypeEntity 
{ 
} 


public class ClassA<T> where T : ITypeEntity 
{ 
    ClassB<T> b = new ClassB<T>(); 
    public void MethodA(IRepo<T> repo) 
    { 
     b.MethodB(repo); 
    } 
} 
public class ClassB<T> where T : ITypeEntity 
{ 
    IRepo<T> repo; 
    public void MethodB(IRepo<T> repo) 
    { 
     this.repo = repo; 
    } 
} 
+0

Si está generic'ing la primera clase también tendrá que ser generic'ed –

+0

la segunda desgracia no tengo la opción de hacer claseA una clase genérica, ya que es un usuario ASP .Net Control y realmente no quiero tratar de resolver ese problema. Gracias por tu pensamiento. Era perfectamente razonable basado en el contexto limitado que di. –

+0

En ese caso, ¿necesita usar genéricos? Me parece que el problema es porque ClassA contiene un objeto de tipo ClassB. Intente implementar primero sin medicamentos genéricos, eso puede ser adecuado. –

0

Este es un uso redundante de los genéricos, si T tan sólo puede ser una instancia de ITypeEntity no debe utilizar los genéricos.

Los genéricos son para cuando tienes varios tipos que pueden estar dentro de algo.

+0

Eso no está del todo bien. T puede ser cualquier objeto que implemente ITypeEntity. Este es un uso correcto de los genéricos. – Metro

+0

Sí, pero debido a que ITypeEntity es una interfaz, puede devolver el objeto a un ITypeEntity en cualquier momento. Entonces, para pasar algo a ese argumento genérico, tiene que implementar ITypeEntity, a su vez "es" ITypeEntity –

1

I get the following error: cannot convert from IRepo<'T> to IRepo<'ITypeEntity>

que está recibiendo este error de compilación porque IRepo<T> y IRepo<ITypeEntity> son no la misma cosa. Este último es una especialización del primero.IRepo<T> es una definición de tipo genérico , en donde el parámetro de tipo T es un marcador de posición, y IRepo<ITypeEntity> es una estructurada de cara tipo genérico de la definición de tipo genérico, en el que el parámetro de tipo T de está especificado para ser ITypeEntity.

I keep thinking that this should compile as I'm constraining T within MethodA to be of type ITypeEntity.

La restricción where no ayuda aquí, ya que sólo contrains del tipo que se puede brindar para T en las llamadas sitios para MethodA.

Aquí es la terminología de la documentación de MSDN (ver Generics in the .NET Framework) que puede ayudar a:

  • Una definición de tipo genérico es un clase, estructura o interfaz declaración que funciona como una plantilla , con marcadores de posición para los tipos que puede contener o usar. Por ejemplo, la clase Dictionary<<K, V> puede contener dos tipos: claves y valores. Como es solo una plantilla, no puede crear instancias de una clase, estructura o interfaz que es una definición de tipo genérico .

  • Parámetros genéricos de tipo, o tipo parámetros, son los marcadores de posición en un tipo genérico o una definición de método. El tipo genérico Dictionary<K, V> tiene dos parámetros de tipo , K y V, que representan los tipos de sus claves y los valores .

  • A construyó tipo genérico, o el tipo construido, es el resultado de tipos que especifican para los parámetros de tipo genérico de un definición de tipo genérico.

  • Un argumento de tipo genérico es cualquier tipo que está sustituido por un parámetro tipo genérico.

  • El término general genérico tipo incluye tanto los tipos construidos como las definiciones de tipo genérico .

  • Restricciones son límites establecidos en parámetros de tipo genérico. Para el ejemplo , puede limitar un tipo de parámetro a los tipos que implementan la IComparer<T> interfaz genérica , para garantizar que se puedan solicitar las instancias del tipo. También puede restringir los parámetros de tipo a que tengan una clase de base particular , que tengan un constructor predeterminado , o que sean tipos 0 tipos de valores de referencia . Los usuarios del tipo genérico no pueden sustituir los argumentos de tipo que no satisfacen las restricciones .

1

Por favor refiérase a la pregunta @monoxide 's

Y as I said there, comprobando serie de mensajes sobre contravarianza y covarianza para los genéricos de Eric Lippert hará mucho más clara de esta.

0

En el contexto de todo tipo de métodos genéricos, permítame darle una función genérica simple. Es un equivalente genérico de IIf de VB() (Inmediato si), que en sí mismo es una pobre imitación del operador ternario de estilo C (?). No es útil para nada, ya que el verdadero operador ternario es mejor, pero tal vez lo ayude a comprender cómo se construyen las funciones genéricas y en qué contextos se deben aplicar.

T IIF<T>(bool Expression, T TruePart, T FalsePart) 
{ 
    return Expression ? TruePart : FalsePart; 
}