Сделать список / коллекцию объектов с открытыми свойствами доступными только для чтения

У меня есть следующая проблема.

Допустим, есть общедоступный Class1, определенный следующим образом:

public class Class1
{
     public string pr1 { get; set; }
     public string pr2 { get; set; }

     public Class1()
     {
     }
}

Мне не разрешено изменять определение класса - оно используется другими людьми, и им нужно, чтобы оно было написано таким образом. И тут приходит беда.

я нуждаюсь static List<Class1> myStaticList который может быть изменен только внутри Class2, а везде - только для чтения. Под словом "только для чтения" я подразумеваю, что пользователь не должен ничего менять, включая свойства элементов списка.

Что-то вроде: Class2.list[0].pr1="newValue" вызываемый извне Class2 должен быть заблокирован.

ReadOnlyCollection не выполняйте работу, потому что пользователь все еще может изменить данный элемент списка, поэтому решение, приведенное здесь List только для чтения с закрытым набором, не удовлетворяет моим потребностям.

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

Единственное решение, которое я нашел, - это создание нового класса:

public class Class1ReadOnly : Class1
{
    public new string pr1{ get; private set; }
    public new string pr2{ get; private set; }
    public Class1ReadOnly(Class1 tmp)
    {
        pr1= tmp.pr1;
        pr2 = tmp.pr2;
    }
}

Но я надеялся, что два найдут лучший способ, так как мне кажется странным определять целый новый класс только из-за одного единственного списка только для чтения

Заранее спасибо за любые подсказки.

Возможно ли это по-другому?


Контекст реального мира выглядит так:

В коде мы редактируем базу данных, и я хочу иметь резервный список записей, который был изменен с их начальными значениями. Я хочу, чтобы он был статичным, чтобы иметь возможность восстанавливать БД везде, где я хочу. Class1 содержит все значения одной записи нашей БД, поэтому List<Class1> Я могу сохранить все измененные записи и восстановить их, если это необходимо. А поскольку это резервное копирование, важно быть доступным только для чтения, потому что непреднамеренные изменения могут привести к катастрофе.

3 ответа

Под "мне не разрешено изменять его", вы имеете в виду, что (как следует из формулировки вопроса) вам не разрешено изменять значения в классе? Или что вам буквально не разрешено изменять само объявление класса? Кроме того, если вам не разрешено изменять объявление класса, как насчет кода, который использует класс в коллекциях? Например бы List<Class1> объект должен оставаться List<Class1>, или вы могли бы заменить Class1 типа с чем-то еще?


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

interface IClass1
{
    string str1 { get; }
    string str2 { get; }
}

Тогда объявите Class1 как реализации этого интерфейса, вложенного внутри Class2:

class Class2
{
    private class Class1 : IClass1
    {
        public string str1 { get; set; }
        public string str2 { get; set; }
    }
}

Теперь, используя методы, которые вы уже видели для того, чтобы сделать сам список доступным только для чтения, вы предотвращаете любой другой код от замены элементов в списке (то есть с какой-то другой реализацией интерфейса). И только Class2 может успешно привести к реализации IClass1 элемент в списке Class1 получить доступ к общедоступному сеттеру для свойств.

Если это не решает вашу проблему, было бы полезно, если бы вы могли более точно определить, какие именно существуют ограничения для решения проблемы.

Учитывая обстоятельства, у вас есть подход, который работает. Вы используете решение, которое является шаблоном адаптера. Это не странно делать что-то подобное.

Шаблон адаптера
умысел
Преобразуйте интерфейс класса в другой интерфейс, ожидаемый клиентами.

- Шаблоны проектирования (ISBN 0-201-63361-2)

Я бы определил такой класс следующим образом:

public interface IClass1 {
  string pr1 { get; }
  string pr2 { get; }
}

internal class Class1Adapter : IClass1 {
  public static Class1Adapter FromClass1(Class1 class1) {
    var pr1 = class1.pr1;
    var pr2 = class1.pr2;
    return new Class1Adapter {pr1 = pr1, pr2 = pr2};
  }

  public string pr1 { get; private set; }
  public string pr2 { get; private set; }
}

Зачем делать выбор, который я сделал?

  • внутренний класс, поэтому я не просочусь в другие сборки. Таким образом, другие команды не могут использовать Class1Adapter если вы явно не выпустили это.
  • Реализовать новый интерфейс IClass1, Вы говорите, что не можете изменить определение Class1, но если бы это не было критическим изменением API... вы могли бы передать его другой команде. Вы могли бы с текущим изменением определения Class1 реализовать IClass1 и без каких-либо других изменений, вы могли бы в своем коде:
var list = new ReadOnlyCollection<IClass1>(source); 

// ... and later, if attempted

list[0].pr1 = "I'm breaking you!"; 

Будет производить:

Свойство или индексатор 'ConsoleApplication19.IClass1.pr1' нельзя назначить - оно доступно только для чтения

Как насчет защищенного набора?

https://msdn.microsoft.com/en-us/library/bcd5672a.aspx?f=255&MSPPError=-2147217396

или вы можете переопределить свойство setter.

:)

Другие вопросы по тегам