Создание интерфейса с List <T>, который позволяет подклассам T - PullRequest
2 голосов
/ 03 июня 2019

Мне дают занятия ContainerA, ContainerB, ElementA и ElementB.Я не могу изменить эти классы по очень веским причинам ™, за исключением того, что я могу добавить интерфейсы.

Мне хотелось бы что-то вроде интерфейса IElements (ожидаемо допустимого), так как это позволило бымне легко зациклить элементы и прочитать свойства из элементов ContainerA или ContainerB, не зная, какая конкретная реализация фактически используется.Нет методов ни на одном из классов.Для классов элементов мне нужен только доступ к свойствам, реализованным ElementA, примером чего является свойство Id.

Какие у меня есть варианты?

public interface IElements
{
    List<ElementA> Elements { get; set; }
}

public class ContainerA : IElements
{
    public List<ElementA> Elements { get; set; }
}

public class ContainerB : IElements
{
    public List<ElementB> Elements { get; set; }
}

public class ElementA
{
    public string Id { get; set; }    
}

public class ElementB : ElementA
{
}

Я надеюсь чего-то достичьпохож на:

IElements container = ...;
foreach (var element in container.Elements)
{
    Console.WriteLine(element.Id);
}

Ответы [ 2 ]

1 голос
/ 03 июня 2019

IEnumerable является ковариантным , то есть IEnumerable<SubtypeOfT> является подтипом IEnumerable<T>.Таким образом, должно работать следующее:

public interface IElements
{
    IEnumerable<ElementA> ReadOnlyElements { get; }
}

public class ContainerA : IElements
{
    public List<ElementA> Elements { get; set; }
    IEnumerable<ElementA> IElements.ReadOnlyElements => Elements;
}

public class ContainerB : IElements
{
    public List<ElementB> Elements { get; set; }
    IEnumerable<ElementA> IElements.ReadOnlyElements => Elements;
}

Вы можете перебирать их следующим образом:

IElements container = ...;
foreach (var element in container.ReadOnlyElements)
{
    Console.WriteLine(element.Id);
}
1 голос
/ 03 июня 2019

Есть много способов нарезать этот торт, но я хотел бы рассмотреть здесь использование обобщений с ограничениями.Я бы также рассмотрел интерфейс (или абстрактный базовый класс) для объекта элемента, например:

public interface IElement
{
    int Id { get; set; }
}

И ваш интерфейс общих элементов будет выглядеть так:

public interface IElements<TElement>
    where TElement : ElementA
{
    List<TElement> Elements { get; set; }
}

Создание классов вашего контейнера примерно так:

public class ContainerA : IElements<ElementA>
{
    public List<ElementA> Elements { get; set; }
}

public class ContainerB : IElements<ElementB>
{
    public List<ElementB> Elements { get; set; }
}

Теперь вы можете получить доступ к свойству Id любого объекта, который реализует ваши интерфейсы, например:

public void DoFoo<TElement>(IElements<TElement> elements)
    where TElement : ElementA
{
    foreach (var element in elements.Elements)
    {
        // This will compile fine
        var id = element.Id;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...