2010-05-17 11 views
8

Es bastante desconcertante descubrir que la restricción de genéricos no se puede convertir en su tipo derivado.¿Por qué la restricción de genéricos no se puede convertir en su tipo derivado?

Digamos que tengo el siguiente código:

public abstract class BaseClass 
{ 
    public int Version 
    { get { return 1; } } 

    public string FixString { get; set; } 

    public BaseClass() 
    { 
     FixString = "hello"; 
    } 

    public virtual int GetBaseVersion() 
    { 
     return Version; 
    } 
} 

public class DeriveClass: BaseClass 
{ 
    public new int Version 
    { get { return 2; } } 
} 

Y adivina qué, este método devolverá un error de compilación:

public void FreeConversion<T>(T baseClass) 
    { 
     if(baseClass.GetType()==typeof(DeriveClass) 
     var derivedMe = (DeriveClass)baseClass; 
    } 

tendría que emitir el baseClass a object primero antes de que yo puede convertirlo a DerivedClass, es decir,

public void FreeConversion<T>(T baseClass) 
    { 
     if(baseClass.GetType()==typeof(DeriveClass) 
     var derivedMe = (DeriveClass)((object)baseClass); 
    } 

Me parece bastante feo. ¿Por qué esto es así?

Respuesta

7

En primer lugar, no debe convertir una variable de tipo base en un tipo derivado. Se supone que no funciona, solo al revés.

En segundo lugar, por qué funciona a través de object, es porque elimina las comprobaciones de tipo de tiempo de compilación. El compilador puede verificar que BaseType no se puede convertir a DerivedType. Pero cuando una variable es object, el compilador lo deja asumiendo que usted sabe lo que está haciendo. Incluso si se compila, el código se bloqueará durante la ejecución.

3

La respuesta es simple: el compilador no puede saber que T en su método FreeConversion se puede convertir en DeriveClass.

+0

¿Por qué el compilador no puede saberlo? Pensé que debería saber porque explícitamente especifico que 'DeriveClass' es una clase heredada para' BaseClass' – Graviton

+0

@Ngu: No, no es así. Usted * prueba * para ver * si * 'T' es de tipo' DeriveClass', y luego - * independientemente de la prueba * - intenta convertir explícitamente 'T' en' DeriveClass'. Recuerde, el compilador * no * ejecuta * su código, simplemente * compila *. Lo que podrías hacer es convertir con una cláusula 'as' en lugar de un molde explícito. 'baseClass as DeriveClass' devolverá la instancia como' DeriveClass' si es posible, y 'null' de lo contrario, pero si comprueba que es posible primero (como lo hace ahora) sabe (aunque su código no) que lo hará nunca ser 'nulo'. –

+0

@Ngu ¡El compilador no puede saber porque nadie puede saberlo! Cuando se crea la plantilla no hay nada que decir que solo recibirá un parámetro de BaseClass y ciertamente nada que decir que será un valor de DerivedClass. No hay nada que impida que alguien llame 'FreeConversion (42)' por ejemplo. La única excepción es un método miembro de una plantilla restringida derivada de su propio parámetro (realmente útil en algunos casos). –

1

Como ya has dicho, el truco más barato es primero lanzarlo para objetar, luego al tipo al que quieres ir. Feo, pero funciona.

Aparte de eso, es posible que estés violando el principio de sustitución de Liskov, nada que perjudique a ningún animal, pero que puede llevar tu diseño a un código inmanejable.

En tercer lugar, un buen truco para que su clase base exponer el tipo derivado es algo como esto:

public class Base<T> where T : Base<T> { 
    T IAmDerived; 
} 

public class Derived : Base<Derived> { } 
0

En primer lugar, en su método genérico del tipo T podría ser un tipo de valle o de referencia tipo. Y la razón por la que te permite hacerlo a través de 'Objeto' es que, simplemente estás haciendo un boxeo-unboxing que funciona para cualquier tipo de sistema.

En segundo lugar, será una idea terrible convertir/convertir un objeto de clase base en una clase derivada. Estás violando la mecánica de OOP.

Si realmente desea devolver un objeto de tipo derivado de la clase base, aquí hay una forma posible: la solución es muy similar a la que Frank ha ofrecido.

//This is how you create a function in BaseClass that returns the collection 
//of DerivedTypes when called from an object of type derivedclass, no magic just Generics. 

//**The BaseClass** 
public class BaseClass<T> 
    where T : BaseClass<T> 
{ 
    public HashSet<T> GetHashSet() 
    { 
     HashSet<T> _hSet = new HashSet<T>(); 
     //do some work    
     //create a HashSet<T> and return;    
     return _hSet; 
    } 
} 
//**The Derived Class** 
public class DerivedClass : BaseClass<DerivedClass> 
{ 
    //you have the method inherited. 
} 
Cuestiones relacionadas