2012-10-26 14 views
5

Dato il codice qui sotto:Come forzare abbattuta sui farmaci generici

class Animal 
{ } 

class Dog : Animal 
{ } 

class Cage<T> 
{ 
    private T animal; 

    public Cage(T animal) 
    { 
     this.animal = animal; 
    } 

    public T Animal 
    { 
     get { return animal;} 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Dog dog = new Dog(); 
     Cage<Animal> animalCage = new Cage<Animal>(dog); 
     Cage<Dog> dogCage = (Cage<Dog>)animalCage; 
    } 
} 

Come posso aggirare l'ultimo errore del compilatore (conversione da animalCage a dogCage)?

Nel mio codice So che la gabbia contiene un cane, ma non riesco a trovare un modo per eseguire il cast. È il mio alternativa unica per creare un convertitore e creare un nuovo gabbia <Dog> esempio dal valore di un Gabbia <Animal>?

+0

Si può sapere che la gabbia contiene un cane, ma il * tipo di sistema * non lo fa. I generici Java ti lasciano fuori con un avviso perché gli argomenti generici vengono cancellati durante la compilazione, ma i generici CLR vengono mantenuti in runtime. –

+0

Hai ragione ... la mia domanda era esattamente se c'è una dichiarazione speciale (o una parola chiave C#) per dire al compilatore "spingere me" :) – user1778378

+0

Nessuna fortuna - non c'è modo di disattivare l'applicazione di tipo generico CLR in fase di esecuzione . –

risposta

0

È possibile aggiungere un generic constraint

class Cage<T> where T : Animal 

e poi basta restituire la classe di base dal vostro metodo di

public Animal Animal 
{ 
    get { return animal;} 
} 

Il vincolo generico informa il compilatore che ci sono dei limiti sui tipi che possono essere forniti per T. Se fornisci qualcos'altro, ad es. Cage<object>, si otterrà un errore in fase di compilazione.

+1

La soluzione non risolve il problema. Il compilatore ha ancora l'errore Impossibile convertire 'Cage ' in 'Cage ' La varianza – user1778378

2

Questo sarebbe un lavoro per la varianza generica, tranne che T dovrebbe essere normalmente covariante (poiché è un'uscita) e si sta tentando di usarlo in modo controvariante. Inoltre, la varianza non è applicabile alle classi, solo alle interfacce e ai delegati, quindi è necessario definire uno ICage<T>.

Covariance consentirebbe il cast nell'altra direzione: è possibile trasmettere ICage<Dog> a ICage<Animal>. La controvarianza porterebbe a una contraddizione, dal momento che si sarebbe in grado di provare a trasmettere un Cage<Animal> contenente un Cat a Cage<Dog>, restituendo get_Animal mal digitato.

Gli array sono covariante così: si può lanciare un Dog[] a un Animal[], ma non si può lanciare un Animal[] ad un Dog[], anche se si sa che contiene solo i cani.

La prossima cosa che ho pensato era definire un operatore di conversione esplicito, ma these can't be generic.

Alla fine, è necessario costruire un nuovo Cage<Dog> per farlo funzionare.

+0

(Co) viene applicata per le interfacce, non per le classi. Potresti voler modificare la tua risposta per riflettere questo. –

+1

Ci ho pensato un po ': a prescindere che la varianza sia limitata ai delegati e alle interfacce, quello che sta cercando è un cast ristretto e non sicuro, e permetterebbe di portare a una contraddizione nel sistema dei tipi. –

4

Problema # 1: Non è possibile attivare un'istanza Cage<Animal> in un'istanza Cage<Dog>, è necessario un esempio Cage<Dog> (o un'istanza di un tipo più specifico) il cui riferimento è memorizzato in una variabile di un tipo meno specifico.

Change

Cage<Animal> animalCage = new Cage<Animal>(dog); 
Cage<Dog> dogCage = (Cage<Dog>)animalCage; 

a

Cage<Animal> animalCage = new Cage<Dog>(dog); 
Cage<Dog> dogCage = (Cage<Dog>)animalCage; 

Problema # 2: non è possibile memorizzare il riferimento a un'istanza Cage<Dog> in una variabile Cage<Animal>, perché le classi non supportano co/controvarianza.

Change

class Cage<T> 
{ 
    ... 

a

interface ICage<out T> 
{ 
    T Animal { get; } 
} 

class Cage<T> : ICage<T> 
{ 

e

Cage<Animal> animalCage = new Cage<Dog>(dog); 
Cage<Dog> dogCage = (Cage<Dog>)animalCage; 

a

ICage<Animal> animalCage = new Cage<Dog>(dog); 
ICage<Dog> dogCage = (Cage<Dog>)animalCage; 

Quindi funziona. (Se non si modifica new Cage<Animal> a new Cage<Dog>, si ottiene un'eccezione di trasmissione in fase di esecuzione.)

+0

Sì, funziona, ma sfortunatamente non sono sicuro di poter passare alle interfacce. L'oggetto Cage deve passare attraverso un servizio WCF e le interfacce si risolvono in "oggetto" generico (con problemi relativi a KnownTypes ...). Scusa, questo è stato un tentativo di semplificazione di un grosso problema :) – user1778378

+0

Non sono sicuro di come possiamo aiutarti. Devi cambiare qualcosa nel tuo design, ma senza saperne di più, è difficile suggerire qualcosa. – dtb

+0

@ user1778378 Problemi relativi a KnownTypes? È possibile sottoclasse ['DataContractResolver'] (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractresolver.aspx) per controllare la mappatura dei tipi di runtime effettivi da e verso XML. –

Problemi correlati