2012-01-17 10 views
8

Considere la clase siguiente:clase genérica y la interacción restricciones de tipo nivel Método

public class DerivedClassPool<TBase> where TBase : class 
{ 
    public TBase Get(Type componentType) 
    { 
     // Not important, but you get the idea 
     return Activator.CreateInstance(componentType) as TBase; 
    } 

    public TDerived SomeMethod<TDerived>() where TDerived : TBase 
    { 
     return Get(typeof(TBase)) as TDerived; 
    } 
} 

Tenga en cuenta que he restringido el argumento de clase genérica TBase a ser una clase: where TBase : class
También he restringido el TDerived genérica El argumento de método es TBase o algo derivado de eso: where TDerived : TBase.

me sale un error en la línea as TDerived:

El parámetro de tipo 'TDerived' no se puede utilizar con el 'como' operador, ya que no tiene una restricción de tipo de clase ni una restricción 'clase'

entiendo que para evitar el error tengo que añadir la restricción class, por lo que tendría:

where TDerived : class, TBase 

¿Por qué tengo que hacer esto cuando TBase ya está restringido para ser una clase y TDerived está limitado a ser un TBase o derivado de él?

+1

Ver http://stackoverflow.com/questions/8002148/c-sharp-generics-contraints-propagation. Eric lo explica por ahí. –

+1

@Jason, creo que se lee mejor con el 'pero'. – Joey

+1

@Joey: Es suficiente. Odio cuando las oraciones comienzan con 'pero', aunque el uso de conjuntos para comenzar las oraciones se considera correcto en estos días. Culpo a mi profesor de inglés de secundaria. Era un fanático de las convenciones clásicas de inglés. –

Respuesta

8

ACTUALIZACIÓN: Esta pregunta era the subject of my blog on September 19th, 2011. Gracias por la gran pregunta!


¿Por qué tengo que hacer esto cuando Tbase ya está forzada a tomar una clase y TDerived está limitado a ser un Tbase o derivados de ella?

Porque un tipo de valor se puede derivar de un tipo de referencia. int se deriva de los tipos de referencia object y System.ValueType e implementa una serie de interfaces. Eso no hace que int sea un tipo de referencia.

Es perfectamente legal para usted llamar al SomeMethod<int> en una instancia de DerivedClassPool<object> porque int se deriva del objeto.

Ahora, no son casos en los que su crítica estaría justificada. Se pueden construir situaciones en las que dos parámetros de tipo están relacionados de tal manera que ambos lógicamente solo pueden ser tipos de referencia, pero solo uno de ellos se clasifica por el idioma como "conocido por ser del tipo de referencia".

Como ejercicio para el lector: ¿puede encontrar uno? Puede ser necesario leer detenidamente la sección 10.1.5 de la especificación para obtener una definición precisa de "se sabe que es un tipo de referencia".

+0

¿Es posible crear nuestros propios tipos de valores derivados de tipos de referencia (en C#)? –

+0

@GeorgeDuckett ¿Por qué es relevante? –

+0

@OskarKjellin: No lo es, solo tengo curiosidad. :) –

2

Porque TBase podría ser una interfaz y, por lo tanto, TDerived podría ser un tipo de valor.

+0

TBase necesita ser una clase? –

+1

seguro de eso? http://msdn.microsoft.com/en-us/library/d5x73970.aspx: 'El argumento de tipo debe ser un tipo de referencia; esto se aplica también a cualquier clase, interfaz, delegado o tipo de matriz. –

+1

No sabía nada de eso. Difícil que 'clase' signifique" tipo de valor "en este caso –

Cuestiones relacionadas