2016-07-28 8 views
0

Рассмотрим иерархию классов, которая выглядит как показано ниже. В сущности, есть аннотация ComplexBase с некоторыми полями, которые являются общими для всех классов. Затем есть ComplexClass, полученный от ComplexBase, который, помимо прочего, содержит коллекцию ComplexElement s, также полученную от ComplexBase.Ковариация для классов со списком в 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 { } 

Реализации абстрактных классов ComplexClassA и ComplexClassB. В коллекции только ComplexElement содержатся только экземпляры ComplexElementA, а только последние 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 { } 

То, что я изо всех сил, чтобы понять, как определить новый класс TheBigCollection, занимающих различные поля и методы, плюс набор экземпляров обоих ComplexClassA и ComplexClassB. Нерабочая версия может выглядеть

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); 
    } 
} 

Конечно, это не компилируется, так как ComplexClass определяются с общим типом.

Моего previous attempt был использовать концепцию сокрытия списков, но оказывается, что это мешает мне получить доступ списков ComplexElements в случаях ComplexClassA или ComplexClassB что я извлекаемые из списка в TheBigCollection.

Я узнал из своего предыдущего сообщения, что ковариация может решить проблему, но я не понимаю, как использование IEnumerable, которое является неизменным, может использоваться для добавления новых элементов tho TheBigCollection в список классов.

+0

Считаете ли вы создание промежуточного класса ComplexClassBase: ComplexBase и наследование ComplexClass из ComplexClassBase? – michauzo

+0

@michauzo Да, я сделал очень многое в ответ на [этот пост] (http://www.gamedev.net/topic/660308-list-of-generic-objects/). Проблема, возникающая в связи с этим решением, заключается в том, что я не могу перебирать список «Классы» и, к примеру, получить доступ к элементу «Элементы.Count» каждого элемента без надлежащего приведения. – Informagic

+0

в промежуточном классе (или интерфейсе) вы можете поместить свойство SortedList Elements {get; задавать; }, а затем реализовать его в ComplexClass с использованием специализированного типа. – michauzo

ответ

0

Я пробовал следующее и работает отлично.

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; } 
    } 
} 

Теперь вы можете использовать IComplexClass в TheBigCollection.

+0

Это кажется приятным - я не знал, что можно переписать getter базового класса, обратившись к нему с использованием имени класса, но проблема здесь тоже. Представьте, что у меня есть запрос Linq для члена 'Classes' экземпляра' TheBigColletcion'. При доступе к элементу элемента результата запроса во время выполнения, getter выдает 'System.InvalidCastException', так как он пытается использовать' SortedList', например, 'ComplexClassA' для 'IDictionary' с' ComplexClass 'ы. – Informagic

+0

Геттер, который предоставляет доступ, как планировалось, и избегает «System.InvalidCastException» - это тот, который помещает листинг внутри 'KeyValuePair', используя' get {return Elements.ToDictionary (el => el.Key, el => (ComplexElement) el.Value); } '. Поразительно. – Informagic

+0

Действительно. Ваше решение не очень элегантно, но оно работает, и я пока не вижу лучшего. – michauzo