2012-09-27 60 views
7

Tengo una situación en la que tengo una clase que acepta una instancia de un determinado tipo de objeto en su parámetro de tipo genérico. El diseño es algo como esto:Devolución genérica sin saber tipo

public abstract BaseClass { ... } 
public DiamondClass : BaseClass { ... } 
public SilverClass : BaseClass { ... } 

public Handler<T> where T : BaseClass { ... } 

Quiero para ser capaz de crear un método para devolver una instancia de Handler<DiamondClass> o Handler<BaseClass> sin definir el tipo al de entrada. He intentado algo en este sentido:

public Handler<BaseClass> GetHandler(HandlerType type) 
{ 
    switch(type) 
    { 
     case HandlerType.Diamond: return new Handler<DiamondClass>(); 
     case HandlerType.Silver: return new Handler<SilverClass>(); 
     default: throw new InvalidOperationException("..."); 
    } 
} 

Pero esto no va a funcionar, porque al parecer Handler<DiamondClass> no rechaza implícitamente a Handler<BaseClass>. Puedo especificar así:

public Handler<T> GetHandler<T>(HandlerType type) where T : BaseClass 
{ 
    switch(type) 
    { 
     case HandlerType.Diamond: return (Handler<T>)new Handler<DiamondClass>(); 
     case HandlerType.Silver: return (Handler<T>)new Handler<SilverClass>(); 
     default: throw new InvalidOperationException("..."); 
    } 
} 

Pero ahora tengo que llamar GetHandler<DiamondClass> o GetHandler<BaseClass>. Y eso frustra el propósito de tener un método que devuelva el controlador adecuado basado en una enumeración, sin saber el tipo. Tenía la esperanza de que pudiera definir un objeto Type y pasarlo, tales como:

Type objType = typeof(DiamondClass); 
var handler = Handler<objType>(); 

Pero al parecer, C# no voy a permitir que ese tipo de locura. He hecho esto de varias maneras diferentes, y me gustaría pensar que hay una manera de hacerlo, pero estoy perplejo.


(que en realidad no conseguir este trabajo mediante la devolución de un objeto dynamic, pero me gustaría evitarlo si es posible, ya que pierde cualquier tipo de seguridad y compatibilidad con IntelliSense.)

+0

¿Qué pasa con 'var handler = Handler ();', no veo el problema que estás tratando de resolver. – CaffGeek

+3

si tiene que verificar el tipo dentro de un método genérico, siempre cuestionaría la utilidad de dicho método. –

+0

@RobA Tenga en cuenta que simplifiqué mucho el ejemplo en aras de la explicación. Parte del problema es que me gustaría que pertenezca a un control de usuario, y no puedo definir ESO como genérico porque mata al diseñador. – KChaloux

Respuesta

9
Este es

donde covarianza entra en juego, covarianza y contra-varianza acaba de trabajar sólo en la interfaz y delegado, por lo que, para resolver su problema, simplemente definir una nueva interfaz IHandler como co-variante con out que especifica que el parámetro de tipo es una variante:

public interface IHandler<out T> where T : BaseClass 
{ 
} 

Una interfaz que tiene un parámetro de tipo covariante permite a sus métodos para volver tipos más derivados de los especificados por el parámetro de tipo

va a trabajar. Más información es here

+2

+1 el linkyyyy – JonH

+0

¡Oye, no sabía que podíamos hacer eso, con el tipo ! Frijoles fríos. Amo aprender estas cosas Editar: solo lo probé. Eres un salvavidas. Simplificó el concepto mucho más de lo que pensé que tenía que hacer. Me preguntaba por qué no aceptaría al niño como un tipo de base. – KChaloux

+0

@KChaloux: porque esto solo funciona con INTERFACE y DELEGATE si te entiendo correctamente? ¿qué significa * el niño como tipo de base *? –

Cuestiones relacionadas