2016-07-28 1 views
0

Considérons une hiérarchie de classes qui ressemble à ci-dessous. En substance, il y a un résumé ComplexBase avec des champs communs à toutes les classes. Puis il y a un ComplexClass dérivé de ComplexBase qui, entre autres choses, détient une collection de ComplexElement s également dérivé de ComplexBase.Covariance pour les classes avec des membres de liste en C#

public abstract class ComplexBase { 
    internal abstract string Identifier { get; } 
} 

public abstract class ComplexClass<T> : ComplexBase where T : ComplexElement { 
    internal SortedList<string, T> Elements { get; set; } 
} 

public abstract class ComplexElement : ComplexBase { } 

des classes Implémentations abstraites sont ComplexClassA et ComplexClassB. La collection ComplexElement de la première contient uniquement des instances de ComplexElementA et de cette dernière seulement ComplexElementB.

public class ComplexClassA : ComplexClass<ComplexElementA> { 
    public ComplexClassA() { 
    Elements = new SortedList<string, ComplexElementA>(); 
    } 
} 

public class ComplexElementA : ComplexElement { } 

public class ComplexClassB : ComplexClass<ComplexElementB> { 
    public ComplexClassB() { 
    Elements = new SortedList<string, ComplexElementB>(); 
    } 
} 

public class ComplexElementB : ComplexElement { } 

Ce que je suis mal à comprendre est comment définir une nouvelle classe TheBigCollection occupant différents domaines et méthodes, ainsi qu'une collection d'instances des deux ComplexClassA et ComplexClassB. Une version non-travail pourrait ressembler à

public class TheBigCollection { 
    internal SortedList<string, ComplexClass> Classes { get; set; } 

    public TheBigCollection() { 
    Classes = new SortedList<string, ComplexClass>(); 
    } 

    public void Add(string name, ComplexClass element) { 
    Classes.Add(name, element); 
    } 
} 

Bien sûr, cela ne compile pas depuis ComplexClass est définie avec un type générique.

Mon previous attempt était d'utiliser un concept de cacher les listes, mais il se trouve que cela me empêche d'accéder aux listes de ComplexElements dans les cas de ComplexClassA ou ComplexClassB que je récupère dans la liste TheBigCollection.

J'ai appris de mon article précédent que la covariance pouvait résoudre le problème, mais je ne comprends pas comment utiliser IEnumerable - qui est immuable - peut être utilisé pour ajouter de nouveaux éléments à la liste des classes TheBigCollection.

+0

Avez-vous envisagé de créer une classe complexe ComplexClassBase: ComplexBase et d'hériter ComplexClass de ComplexClassBase? – michauzo

+0

@michauzo Oui, je l'ai fait, très bien dans la ligne de la réponse à [ce poste] (http://www.gamedev.net/topic/660308-list-of-generic-objects/). Le problème qui se pose avec cette solution est que je ne peux pas itérer sur la liste 'Classes' et accéder, par exemple, à la propriété' Elements.Count' de chaque élément sans un cast approprié. – Informagic

+0

dans la classe intermédiaire (ou l'interface) vous pouvez mettre la propriété SortedList Elements {get; ensemble; } puis l'implémenter dans ComplexClass en utilisant un type spécialisé. – michauzo

Répondre

0

J'ai essayé ce qui suit et cela fonctionne très bien.

internal interface IComplexClass 
{ 
    IDictionary<string, ComplexElement> Elements { get; } 
} 

public abstract class ComplexClass<T> : ComplexBase, IComplexClass where T : ComplexElement 
{ 
    internal SortedList<string, T> Elements { get; set; } 

    IDictionary<string, ComplexElement> IComplexClass.Elements 
    { 
     get { return (IDictionary<string, ComplexElement>)Elements; } 
    } 
} 

Vous pouvez maintenant utiliser IComplexClass dans TheBigCollection.

+0

Cela semble bien - je ne savais pas que l'on pouvait écraser le getter de la classe de base en y faisant référence en utilisant le nom de la classe - mais il y a aussi un problème ici. Imaginez que j'ai une requête Linq sur le membre 'Classes' d'une instance de' TheBigColletcion'. Lors de l'accès à 'Elements' d'un élément du résultat de la requête au moment de l'exécution, le getter lève une' System.InvalidCastException' puisqu'il essaye de lancer un 'SortedList' avec, par exemple,' ComplexClassA's à un 'IDictionary' avec' ComplexClass 'es. – Informagic

+0

Un getter qui donne accès comme prévu et évite le 'System.InvalidCastException' est celui qui place le casting dans le' KeyValuePair' en utilisant 'get {return Éléments.ToDictionary (el => el.Key, el => (ComplexElement) el.Value); } '. Étonnant. – Informagic

+0

En effet. Votre solution n'est pas très élégante mais ça marche et je n'en vois pas de meilleur pour l'instant. – michauzo