C # Наследование списков интерфейсов - PullRequest
1 голос
/ 15 марта 2019

Я немного новичок в интерфейсах C # и столкнулся с довольно неуклюжим решением проблемы расширения в списках наследуемых интерфейсов.Примеры интерфейсов выглядят так:

public interface IData
{
    int Value { get; set; }
}

public interface IDataWithName : IData
{
    string Name { get; set; }
}

public interface IDataContainer
{
    IList<IData> DataList { get; set; }
}

public interface IDataWithNameContainer : IDataContainer
{
    new IList<IDataWithName> DataList { get; set; }
}

С методом расширения:

public static class ExtensionMethod
{
    public static int CountNumberOfIDataItems(this IDataContainer i)
    {
        return i.DataList.Count();
    }
}

При реализации этих интерфейсов необходимо реализовать как IList DataList, так и IList IDataContainer.DataList.Хотя это возможно, полученный код неэлегатен:

public class DataNameImplimentatioFixed : IDataWithNameContainer
{
    public IList<IDataWithName> DataList { get; set; }
    IList<IData> IDataContainer.DataList
    {
        get => new List<IData>(DataList);
        set
        {
            DataList = new List<IDataWithName>();
            foreach (IDataWithName _dataLoop in value) { DataList.Add(_dataLoop); }
        }
    }
}

Дублирование списка каждый раз, когда вызывается расширение, может привести к некоторым проблемам с производительностью, а также к необходимости дополнительной проверки того, что любые дополнительные методы в IDataContainerне будет пытаться добавить классы, которые наследуются от IData, но не IDataWithName.

Такое ощущение, что должен быть лучший способ использования методов расширения, который не столь уязвим для проблем с будущими расширениями.Лучшее решение, которое я могу придумать, - это удалить IList DataList из IDataContainer и иметь отдельный метод расширения для каждого класса, который содержит список.

Может кто-нибудь придумать лучшее решение, чем это?

Ответы [ 2 ]

3 голосов
/ 15 марта 2019

Проблема в том, что IDataWithNameContainer скрывает свойство DataList, которое оно наследует от IDataContainer, а не метод расширения.Так не должно быть.

Простой способ создать производный контейнерный класс, который принимает только конкретную реализацию IData, состоит в том, чтобы сделать интерфейсы контейнера универсальными и использовать более строгое ограничение типа в IDataWithNameContainer:

public interface IData
{
    int Value { get; set; }
}

public interface IDataWithName : IData
{
    string Name { get; set; }
}

public interface IDataContainer<T> where T:IData
{
    IList<T> DataList { get; set; }
}

public interface IDataWithNameContainer<T> : IDataContainer<T> where T:IDataWithName
{

}

public static class ExtensionMethod
{
    public static int CountNumberOfIDataItems<T>(this IDataContainer<T> i) where T:IData
    {
        return i.DataList.Count;
    }
}

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

public class Boo:IDataWithName 
{
    public int Value { get; set; }
    public string Name { get; set; }
}


public class BooContainer: IDataWithNameContainer<Boo>
{
    public IList<Boo> DataList { get; set; }
}

Обновление

Кстати, нет причин добавлять новые элементы в DataList в цикле.Конструктор List может принимать IEnumerable с начальными значениями:

public class BooContainer: IDataWithNameContainer<Boo>
{
    IList<Boo> _list=new List<Boo>();
    public IList<Boo> DataList 
    { 
        get => _list; 
        set => _list=new List<Boo>(value); 
    }
}

Это позволяет хранить списки и массивы в DataList, копируя их содержимое в новый список.

Thisбросит, если кто-то установит ноль.Чтобы избежать этого, можно использовать нулевой оператор замены в установщике:

    set => _list=new List<Boo>(value ?? new Boo[0]); 
0 голосов
/ 15 марта 2019

Вам не нужно IDataContainer и IDataWithNameContainer. У вас уже есть значения в интерфейсе IDataWithName, так почему же IDataWithNameContainer также наследуется от IDataContainer? Разве не поэтому вы сделали дополнительный интерфейс?

IData
IDataWithName : IData
IList<IDataWithName>  //This already has a list of (name) and (value)

Эти вопросы не требуют ответа; они часть этого ответа, который, если быть грубым, просто плохой дизайн.

Вы перепроектировали интерфейс, чтобы упростить другую работу (что, наверное, хорошо), но затем вы добавили другую работу: /

Просто сделайте ваш класс таким:

public interface IDataWithNameContainer // Do not add this here -> : IDataContainer
{
    IList<IDataWithName> DataList { get; set; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...