Предоставление элементов коллекции только для чтения

У меня есть бизнес-объекты, как показано ниже,

class Class1
{
   List<Class2> classes = new List<Class2>();

   public IEnumerable<Class2> Classes { get { return classes.AsEnumrable(); }

   public void AddClass(Class2 cls)
   {
       classes.Add(cls);
   }
}

class Class2
{
    public string Property { get; set; }
}

Моя бизнес-логика требует, чтобы после добавления экземпляра Class2 с помощью метода AddClass в верхнюю часть списка Classes в Class1 никто не мог редактировать свойства экземпляров Class2, добавленных ранее в список, только последний элемент в списке. список можно редактировать. Как мне это сделать?

Я пробовал IReadOnlyList, но похоже, что он связан с тем, чтобы сделать структуру списка нередактируемой, не предотвращая редактирование содержимого его элементов.


person Sisyphus    schedule 11.02.2018    source источник
comment
Я думаю, что структура данных, которая вам нужна, — это стек, то есть First In Last Out (FILO). Это означает, что из стека может быть извлечен только последний элемент. Другие элементы не могут быть извлечены до тех пор, пока не будут удалены элементы над ними.   -  person benjrb    schedule 11.02.2018
comment
Какова основная проблема бизнеса здесь?   -  person Stephen Kennedy    schedule 11.02.2018
comment
Похоже, вам нужно, чтобы ваш Class2 имел свойства только для чтения или какую-то блокировку/разблокировку для поведения редактирования. Вы также можете сделать его виртуальным и написать какой-нибудь прокси-класс, который предотвратит редактирование, но это не похоже на правильное архитектурное решение. Это не имеет ничего общего с типом коллекции, так как коллекция просто возвращает ссылки на Class2 объектов.   -  person Yeldar Kurmangaliyev    schedule 11.02.2018


Ответы (4)


Задача контейнера не в том, чтобы диктовать поведение своих элементов. Контейнер — это просто объект, который содержит другие объекты. IReadOnlyList — это контейнер, элементы которого нельзя изменить. Но элементы внутри него являются просто Class2 экземплярами - контейнер ничего не может сделать, чтобы предотвратить их редактирование.

Учти это:

myReadOnlyCollection[0].Property = "blah";
var firstItem = myReadOnlyCollection[0];
firstItem.Property = "blah";

Должно ли это быть законным в вашем сценарии? В чем разница между ними? firstItem — это просто экземпляр Class2, который понятия не имеет, что когда-то был внутри коллекции, доступной только для чтения.

Вам нужно, чтобы сам Class2 был неизменным. Это зависит от класса, а не от контейнера. Прочтите о неизменности, которая является важной концепцией для понимания, и реализуйте Class2 соответствующим образом. Если вам нужен обычный изменяемый Class2 для изменения его поведения, возможно, добавьте к нему метод ToImmutable(), который возвращает другой элемент без сеттера.

person Avner Shahar-Kashtan    schedule 11.02.2018

Почему вы разоблачаете IReadOnlyCollection. После того, как вы выставили объекты, сами объекты должны быть неизменяемыми.

Почему бы просто не выставить единственный объект, который вы хотите выставить?

   private IEnumerable<Class2> Classes { get { return classes; }

   public Class2 Class2Instance { get { return classes.Last(); } }
person bashrc    schedule 11.02.2018

Я вижу только три варианта. Один из них - изменить Class2, чтобы сделать его запираемым, а затем заблокировать его, как только он будет добавлен в ваш список...

class Class1 {
   List<Class2> classes = new List<Class2>();

   public IEnumerable<Class2> Classes {
      get { return classes.AsEnumrable();
   }

   public void AddClass(Class2 cls) {
      cls.Lock();
      classes.Add(cls);
   }
}

class Class2 {
    private string _property;
    private bool _locked;

    public string Property {
       get { return _property; }
       set {
          if(_locked) throw new AccessViolationException();
          _property = value;
       }
    }

    public void Lock() {
       _locked = true;
    }
}

Другой вариант - возвращать только значения объектов списка вместо самих объектов...

class Class1 {
   List<Class2> classes = new List<Class2>();

   public IEnumerable<string> Values {
      get { return classes.Select(cls => cls.Property); }
   }

   public void AddClass(Class2 cls) {
      classes.Add(cls);
   }
}

В этом втором методе все, кроме одного значения, и вам нужно либо вернуть кортеж. В качестве альтернативы вы можете создать специальный контейнер для Class2, который предоставляет значения только для чтения...

class Class2ReadOnly {
   private Class2 _master;

   public Class2ReadOnly(Class2 master) {
      _master = master;
   }

   public string Property {
      get { return _master.Property; }
   }
}

class Class1 {
   List<Class2ReadOnly> classes = new List<Class2ReadOnly>();

   public IEnumerable<Class2ReadOnly> Classes {
      get { return classes.AsEnumerable(); }
   }

   public void AddClass(Class2 cls) {
      classes.Add(new Class2ReadOnly(cls));
   }
}
person dynamichael    schedule 11.02.2018

Как уже было сказано, это не работа коллекций, чтобы диктовать, если (и как) вы получаете доступ к своим элементам. Я вижу способы обойти это:

Исключения и ссылки:

Измените Class2, чтобы он мог ссылаться на Class1. Если ссылка установлена, бросайте excetpions на все сеттеры. Измените Class1.AddClass, чтобы установить это свойство.

Более мягкой версией этого было бы свойство «только для чтения» в Class2, которое должен проверять весь остальной код.

Свойства и конструкторы только для чтения:

Просто всегда давайте свойства Class2 только для чтения (частный набор). Если вы хотите определить значения свойств, вы должны сделать это в конструкторе (у которого есть правильные аргументы). Этот шаблон активно используется классами исключений.

Художественные махинации с наследованием:

Создайте несколько версий Class2 в цепочке наследования, чтобы Class2Writebale можно было преобразовать в Class2ReadOnly.

Принять неверный Y:

Возможно, вы столкнулись с проблемой XY: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem Если это так, вернитесь на шаг назад, чтобы исправить это.

person Christopher    schedule 11.02.2018