2012-09-27 23 views
7

Ho una situazione in cui ho una classe che accetta un'istanza di un certo tipo di oggetto nel suo parametro di tipo generico. Il layout è qualcosa di simile:Ritorno generico senza conoscere il tipo

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

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

voglio per essere in grado di creare un metodo per restituire un'istanza di Handler<DiamondClass> o Handler<BaseClass> senza definire il tipo di sopra di ingresso. Ho provato qualcosa in questo senso:

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("..."); 
    } 
} 

Ma questo non funzionerà, perché a quanto pare Handler<DiamondClass> non rigetta implicitamente Handler<BaseClass>. Posso specificare in questo modo:

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("..."); 
    } 
} 

Ma ora ho bisogno di chiamare GetHandler<DiamondClass> o GetHandler<BaseClass>. E ciò vanifica lo scopo di avere un metodo che restituisce il gestore corretto basato su un enum, senza conoscere il tipo. Speravo che potrei definire un oggetto Type e passarlo, come ad esempio:

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

Ma a quanto pare C# non permetterò quel tipo di follia. Ho fatto questo in molti modi diversi, e mi piacerebbe pensare che ci sia un modo per farlo, ma sono perplesso.


(in realtà ho fatto ottenere questo lavoro restituendo un oggetto dynamic, ma mi piacerebbe evitare, se possibile, in quanto perde qualsiasi tipo di sicurezza e il supporto Intellisense.)

+0

Cosa c'è di sbagliato con 'var handler = Handler ();', Non vedo quale problema stai cercando di risolvere. – CaffGeek

+3

se devi controllare il tipo all'interno di un metodo generico, metterei sempre in dubbio l'utilità di tale metodo. –

+0

@RobA Tenere presente che ho semplificato molto l'esempio per motivi di spiegazione. Parte del problema è che mi piacerebbe appartenere a un controllo utente, e non posso definirlo come generico perché uccide il progettista. – KChaloux

risposta

9

Questo è dove covarianza entra in gioco, covarianza e contra-varianza solo funziona solo su interfaccia e delegato, quindi, per risolvere il problema, basta definire una nuova interfaccia IHandler come co-variante con out che specifica che il parametro di tipo è co-variante:

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

Un'interfaccia che ha un parametro di tipo covariante permette ai suoi metodi per restituire tipi più derivati ​​da quelli specificati dal parametro type

funzionerà. Ulteriori informazioni sono here

+2

+1 il linkyyyy – JonH

+0

Ehi, non sapevo che potessimo farlo, con il tipo ! Fagioli freschi. Adoro imparare questa roba. Modifica: ho appena provato. Sei un salvagente. Ha semplificato il concetto molto più di quello che pensavo di dover fare. Stavo WONDERING perché non avrebbe accettato il bambino come un tipo di base. – KChaloux

+0

@KChaloux: perché questa roba funziona solo con INTERFACE e DELEGATE se ti capisco correttamente? cosa significa * il bambino come un tipo di base *? –

Problemi correlati