Сделать список / коллекцию объектов с открытыми свойствами доступными только для чтения
У меня есть следующая проблема.
Допустим, есть общедоступный 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.
:)