2011-03-25 28 views
5

que utilizo en mi biblioteca de tres clases:C# tipos genéricos

public abstract class Base<TFirst, TSecond> 
{ 
    public Base() 
    { 
     // actions with ID and Data of TFirst and TSecond 
    } 
} 

public abstract class First<TFirstID, TFirstData> 
{ 
    public TFirstID ID {get; set;} 
    public TFirstData Data {get; set;} 
} 

public abstract class Second<TSecondID, TSecondData> 
{ 
    public TSecondID ID {get; set;} 
    public TSecondData Data {get; set;} 
} 

¿Cómo puedo especificar que TFirst debe heredar de la Primera y TSecond debe heredar de la Segunda, no utilizando los tipos genéricos de identificación y datos de la base de ?

De esta manera:

public abstract class Base<TFirst, TSecond> 
    where TFirst : First // without generic-types 
... 

Editar: En las clases Primera, Segunda y utilizo TFirstID TSecondID de propiedades. En la clase Base, uso estas propiedades.

+0

¿Podría aclarar la pregunta un poco, por favor? ¿Primera base? – foson

+0

Quieres decir que ** no ** quieres 'donde primero: ' No creo que puedas hacer eso, creo que haskell permite algo así, no estoy seguro. – gideon

+0

Foson: No, primero no heredó Base –

Respuesta

1

Eso puede ser complicado si dependen de firmas con esos elementos. Probablemente diría crear una interfaz o base abstracta sin las firmas de tipo. Interfaz más probable.

2

lo general, en un caso como este, voy a construir otra clase base (no genérico) para First<TFirstID, TFirstData> derivar de, por lo que:

public abstract class First{} 

public abstract class First<TFirstID, TFirstData> 
    : First 
{ 
} 

entonces usted puede poner un where TFirst : First en su declaración. No es perfecto, pero funciona si tienes cuidado. Pero puede ser complicado, dependiendo de lo que intentes lograr: pierdes todo el genérico del tipo restringido.

6

No hay manera de que usted puede hacer esto que no sea mediante la introducción de una jerarquía de clases paralelo sin geherics y hacer algunas comprobaciones en tiempo de ejecución:

public abstract class Base<TFirst, TSecond> 
    where TFirst : First 
{ 
    static Base() 
    { 
     if(!typeof(TFirst).IsGenericType || 
      typeof(TFirst).GetGenericTypeDefinition() != typeof(First<,>)) 
      throw new ArgumentException("TFirst"); 
    } 
} 

public abstract class First { } 
public abstract class First<TFirstID, TFirstData> : First 
{ 
} 

Alternativamente, puede reemplazar First con una interfaz de marcador (IFirst).

La comprobación del tiempo de ejecución es posible debido al hecho de que se invocan constructores estáticos para cada closed generic type.

+0

+1 ¡Muy interesante! Pero entonces no puede restringir el genérico 'TFirst' a ** ser un **' First ', podría ser cualquier cosa que herede de' abstract First'. Creo que esto podría ser un patrón anti no? – gideon

2

Una solución sería tener Primera y Segunda mismos implementan una interfaz que no depende de los parámetros de tipo genérico:

public interface IFirst 
{ 
} 

public abstract class First<TFirstID, TFirstData> : IFirst 
{ 
} 

A continuación, asegurar que el parámetro de tipo de base debe utilizar IFirst

public abstract class Base<TFirst, TSecond> 
    where TFirst : IFirst 
+0

Esta es la única otra forma de evitar el uso de la clase real al tiempo que permite el uso de los tipos genéricos como algo distinto de Object. – KeithS

0

Así es como lo harías, si es posible; especifique restricciones de tipo genérico que permitan al compilador detectar usos no válidos de los parámetros genéricos de Base.

Si, por alguna razón, no puede usar restricciones de tipo genérico, la única otra manera de aplicar la verificación de tipo sería agregar comprobaciones en tiempo de ejecución a su lógica que lanzarían una excepción si el genérico creado especificando tipos genéricos no válidos:

public abstract class Base<TFirst, TSecond> 
{ 
    public Base() 
    { 
     if(!typeof(TFirst).IsAssignableFrom(typeof(First)) 
      throw new InvalidOperationException("TFirst must derive from First."); 
     if(!typeof(TSecond).IsAssignableFrom(typeof(Second)) 
      throw new InvalidOperationException("TSecond must derive from Second."); 
    } 
} 

El código anterior es un olor a código grave. El objetivo de los genéricos es permitir que una clase trabaje con muchas clases internas diferentes, al tiempo que permite al compilador asegurarse de que los tipos de parámetros utilizados sean tales que la clase genérica pueda funcionar con ellos. Y además, aún debe poder hacer referencia al espacio de nombres de Primero y Segundo (que supongo que es la razón por la que no puede usarlos como parámetros de tipo genérico en primer lugar).

+0

Creo que su código debería verse más o menos así: 'typeof (TFirst) .IsAssignableFrom (typeof (First <,>))' –

+0

Probablemente, pero mi punto es que todo es un olor a código; las restricciones de tipo genérico deberían implementarse a casi cualquier costo para evitar cosas como esta. – KeithS

+0

Claro, estoy de acuerdo contigo allí. Pero si publica el código, asegúrese de que compila. De lo contrario, no lo publique en primer lugar. –