проблема c# UOD с общими коллекциями и интерфейсами

#c# #oop #generics #collections #interface

Вопрос:

Я пытаюсь смоделировать проблему таким образом, чтобы:

  1. Расписание.Коллекция продуктов может состоять как из: iProducts, так и из IProductsWithConfig
  2. ProductWithConfig.Коллекция конфигурации может состоять только из iProducts, но не IProductsWithConfig

И я немного застрял — как сейчас (ниже) Я не могу запретить добавление/включение IProductsWithConfig в ProductWithConfig.Конфигурация. Есть идеи, как это элегантно закодировать?

     interface ISchedule
    {
        IEnumerable<IProduct> Products { get; set; }
    }

    interface IProductWithConfig : IProduct
    {
        IEnumerable<IProduct> Config { get; set; }
    }

    interface IProduct
    {
        int Id { get; set; }
    }
 

Комментарии:

1. Я не знаю, как это вписалось бы в ваш дизайн, но если бы у вас был другой интерфейс, который наследует IProduct , скажем IPlainProduct , то это было is-a IProduct бы, но нет is-a IProductWithConfig … и ты мог бы IEnumerable<IPlainProduct> Config { get; set; }

2. @Fildor но IPlainProduct будет пустым интерфейсом, который наследуется от IProduct?

3. Может быть. Но в этом нет ничего плохого.

4. На самом деле, я думаю, что пункт 2 нарушает Принцип замены Лискова, если я не ошибаюсь.

5. В основном проблема здесь в следующем: вы разработали «наличие конфигурации», подразумевающее «быть потенциальным элементом конфигурации». Но в вашем втором требовании вы делаете «наличие конфигурации» и «быть конфигурацией» односторонним эксклюзивом. И размышляя об этом и моем решении выше: этого может быть даже недостаточно. Что делать, если у вас есть класс, который реализует оба интерфейса (что абсолютно законно)? …

Ответ №1:

Идея наследования заключается в том, что вы можете использовать любую производную версию IProduct в любой ситуации, когда вам требуется IProduct. Каждый экземпляр класса, реализующего IProductWithConfig, по определению является экземпляром, реализующим IProduct. Комментарий, который сделал Фильдор, имеет смысл: сделайте IProduct абстрактным и создайте из него нового потомка.

Или вы можете скрыть конфигурацию общедоступных свойств и предоставить их с помощью нескольких методов, таких как Add(продукт) и IEnumerable только для чтения. Затем вы можете контролировать, какие объекты добавляются в коллекцию.

Комментарии:

1. Я надеялся, что смогу использовать какой-нибудь трюк с co/contra-дисперсией, с которой я еще не так хорошо знаком.

2.@adam.k противоположность или несоответствие просто удерживает вас от присвоения a List<IDerivedFromIProduct> вашему IEnumerable<IProduct> , но я могу с радостью добавить объект к этому перечислимому до тех пор, пока он is-a IProduct . Это означает, что где-то в дереве он реализует этот интерфейс.