2015-09-10 16 views
6

ho due interfacce:C# Generics, interfacce ed ereditarietà

public interface IAmA 
{ 
} 

public interface IAmB<T> where T : IAmA 
{ 
} 

E due classi che implementano queste interfacce come questo:

public class ClassA : IAmA 
{ 
} 

public class ClassB : IAmB<ClassA> 
{ 
} 

Quando si cerca di utilizzare queste classi come mostrato:

public class Foo 
{ 
    public void Bar() 
    { 
     var list = new List<IAmB<IAmA>>(); 
     list.Add(new ClassB()); 
    } 
} 

Viene visualizzato questo errore del compilatore:

cannot convert from 'ClassB' to 'IAmB<IAmA>'

so di poter fare il compilatore felice con:

public class ClassB : IAmB<IAmA> 
{ 
} 

Ma ho bisogno di essere in grado di essere il parametro Type per IAmB<> in ClassB un'implementazione di IAmA.

+1

Che cosa significa "passare un'implementazione di IAmA"? ClassB non * eredita nulla da ClassA, deve ancora implementare ogni membro di IAmB. Perché * deve * ereditare da IAmB anziché IAmB ? Qual è il vero problema che stai cercando di risolvere? –

+0

Ho aggiornato la domanda. – Alex

+0

l'aggiornamento non ha spiegato nulla - è chiaro fin dall'inizio che si desidera utilizzare la classe concreta - la domanda è * perché *? –

risposta

11

La risposta più immediata è che si possibile fare ciò che si chiede dichiarando il parametro tipo di IAmB<T> come covariante, solo se il tipo è utilizzato come un tipo di ritorno:

public interface IAmB<out T> where T : IAmA 
{ 
    T SomeMethod(string someparam); 
} 

out T significa che puoi usare un tipo più specifico di quello specificato nei vincoli.

Non sarà possibile utilizzare T come parametro. Quanto segue non compilare:

public interface IAmB<out T> where T : IAmA 
{ 
    void SomeMethod(T someparam); 
} 

Dalla documentazione

You can use a covariant type parameter as the return value of a method that belongs to an interface, or as the return type of a delegate. You cannot use a covariant type parameter as a generic type constraint for interface methods.

Questo non è un capriccio del compilatore. Supponendo che si possa dichiarare un parametro del metodo covariante, l'elenco finirebbe con contenere alcuni oggetti che non possono gestire un parametro IAmB<IAmA> - si aspetterebbero un input di ClassA o più specifico. Il tuo codice verrebbe compilato ma fallire in fase di runtime.

Che pone la domanda: perché si desidera utilizzare IAmB<ClassA>?

Si dovrebbe pensare prima di utilizzare questo, anche se ci possono essere altro, modi più adatti per affrontare il tuo problema reale. È inusuale utilizzare un'interfaccia generica che implementa un tipo concreto ma che tenta di utilizzarlo come se implementasse un'altra interfaccia.

È possibile controllare la sezione del documentazione MSDN su Covariance and Contravariance così come Eric Lippert di risposte una di Jon Skeet a this SO question: Difference between Covariance and Contravariance

3

Risposte rapide: rendere il tipo covariante generico (vedi msdn) nell'interfaccia

public interface IAmB<out T> where T : IAmA 
{ 
} 

questo risolverà il problema del compilatore.

Ma questo non risponderà allo why chiesto da Panagiotis Kanavos!

2

Il trucco sta facendo il tipo di vincolo T su IAmB<T>covariante, con la parola chiave out:

public interface IAmB<out T> where T : IAmA 
{ 
} 

Ciò consente di utilizzare un tipo più specifico di quello specificato in origine, in questo caso consente di assegnare un IAmB<ClassA> a una variabile di tipo IAmB<IAmA>.

Per ulteriori informazioni, vedere the documentation.

0

Ho solo spiegato perché questo errore è stato segnalato.

se il vostro IAmB ha un metodo di

public interface IAmB<T> where T : IAmA 
{ 
    void foo(T p); 
} 

public class ClassB : IAmB<ClassA> 
{ 
    void foo(ClassA p) 
    { 
     p.someIntField++; 
    } 
} 

e abbiamo un'altra classe

public class ClassC : IAmB<ClassA2> 
{ 
    void foo(ClassA2 p) 
    { 
     p.someOtherIntField++; 
    } 
} 

e noi assumere List<IAmB<IAmA>>.Add(T p) attuare come questo

IAmA mParam = xxxx; 
void Add(IAmB<IAmA>> p){ 
    p.foo(mParam); 
} 

pensare tutto compilare OK. si passa un'istanza ClassB per List.Add, diventa

void Add(IAmB<IAmA>> p){ 
    //p is ClassB now 
    p.foo(mParam);//COMPILER CAN NOT MAKE SURE mParam fit ClassB.foo 
} 
+0

Questa non è una risposta reale. –

0

Si può essere risolto utilizzando controvarianza e covarianza.

public interface IAmA 
{ 
} 

**public interface IAmB<out T> where T : IAmA 
{ 
}** 


public class ClassA : IAmA 
{ 
} 

public class ClassB : IAmB<ClassA> 
{ 
} 


public class Foo 
{ 
    public void Bar() 
    { 
     var list = new List<IAmB<IAmA>>(); 
     **list.Add(new ClassB());** 
    } 
} 

Ora non si ottiene l'errore del compilatore. Il compilatore è felice.

+0

Non proprio - la covarianza consente solo di usare T come tipo di ritorno. –