2017-06-07 7 views
0

J'essaie d'obtenir quelque chose de travail et de lutte avec ce qui suit en utilisant Contravariance. Ma compréhension est Covariance est où vous pouvez retourner un type dérivé d'un type de base. La contravariance est l'endroit où vous pouvez passer un type dérivé d'un type de base en tant qu'argument dans une classe.C# .net 4.0 Covariant vs Contravariante

J'ai donc l'interface ci-dessous (contravariant):

public interface IBase<in T> where T: BaseModel 
{ 
     void Process(T model); 
} 

J'ai alors une classe abstraite

public abstract class Base<T>: IBase<T> where T: BaseModel 
{ 
    public virtual void Process(T model) 
    { 
     // throw new System.NotImplementedException(); 
    } 
} 

et une autre classe concrète

public class Parent: Base<ParentModel> 
{ 
    public override void Process(ParentModel model) 
    { 
     // throw new System.NotImplementedException(); 
    } 
} 

Considérant le type générique est seulement jamais utilisé comme une entrée et non un type de retour, je ne vois pas pourquoi je ne peux pas faire ce qui suit:

IBase<BaseModel> baseContravariant = new Parent(); 
// This doesn't compile. I will eventually have a list of IBase<BaseMode> to which I'd like to pass in different parent instances. 

J'ai un autre exemple utilisant covariance qui est ci-dessous et fonctionne bien.

public interface IBase<out T> where T : BaseModel, new() 
{ 
    T ProcessAndGet(); 
} 

Résumé

public abstract class Base<T>: IBase<T> where T: BaseModel, new() 
{ 
    public virtual T ProcessAndGet() 
    { 
     var result = new T() as BaseModel; 

     // More shizzle here 
     return (T)result; 
    } 
} 

Béton

public class Parent : Base<ParentModel> 
{ 
    public override ParentModel ProcessAndGet() 
    { 
     var x = base.ProcessAndGet(); 
     return x; 
    } 
} 

Maintenant, je peux faire

IBase<BaseModel> baseInstance = new Base<BaseModel>(); 
IBase<BaseModel> derived = new Parent(); 
baseInstance = derived; 

Il y a plus de code pour les exemples ci-dessus, mais je l'ai enlevé pour faciliter lire (heureusement!) :-)

Répondre

0

La contravariation dans ce cas signifie que vous devez passer des types qui sont du type spécifié ou qui sont "plus spécialisés" (=> dériver du type de base).

Étant donné que votre implémentation Parent dans votre premier exemple ne peut traiter que ParentModel, il est impossible de transmettre une instance BaseModel. Essayer de faire new Parent().Process(new BaseModel()) ne compilerait pas non plus. Donc, il est invalide de le lancer à IBase<BaseModel>. (en supposant que ParentModel est une sous-classe de BaseModel).

Dans ce cas, le modèle de contravariance est plus facile à raisonner en pensant qu'un IBase<in T> "consomme un T". Donc, un IBase<ParentModel> "consomme ParentModel s". Cela signifie qu'il ne peut être passé que des valeurs qui sont des instances de ParentModel ou peuvent être traitées comme une seule (en réalité seulement des sous-classes).

Dans votre deuxième exemple, vous utilisez <out T>, qui est "covariant". Cela peut être décrit comme "il produit des instances de T". Donc, une classe qui "produit" un ParentModel est automatiquement un "producteur" de BaseModel ainsi: puisque ParentModel peut être classé en BaseModel, IBase<ParentModel> peut également être casté en IBase<BaseModel>.

+0

Merci pour la réponse et la réponse informative. Le deuxième exemple que j'ai fourni fait la même chose en termes d'étapes. La classe Parent ne prend que ParentModel mais je peux toujours l'assigner à la classe de base? Je pensais que c'était l'intention derrière la fonctionnalité in/out ... – tones

+0

J'ai ajouté un paragraphe sur le type de covariant.Je ne suis pas tout à fait sûr de ce que votre vraie question pour être honnête .. (autre que de l'expliquer en termes plus faciles, tout remplacer par "producteur" et "consommateur" m'a toujours aidé à comprendre ce truc) –

+0

Aah qui a du sens . Il n'y avait pas de question sur le second point. C'était comme si vous en disiez plus pour comparer les deux pour comprendre comme on a travaillé et l'autre pas. En bref pour la covariance, vous pouvez attribuer à des types moins spécifiques, mais la contravariance vous ne pouvez pas. J'espère avoir compris cela correctement. – tones