Ограничение доступа к вызовам методов для свойств, доступных только для чтения

У меня есть класс, который определяет свойство только для чтения, которое эффективно предоставляет частное поле, примерно так:

public class Container
{
    private List<int> _myList;

    public List<int> MyList
    {
        get { return _myList;}
    }

    public Container() : base ()
    {
        _myList = new List<int>();
    }

    // some method that need to access _myList
    public SomeMethod(int x)
    {
         _myList.Add(x);
    }
}

теперь потребитель не может напрямую управлять моей собственностью, поэтому используйте код вроде aContainer.MyList = new List (); генерирует ошибку времени компиляции. Однако потребитель может абсолютно свободно вызывать всевозможные методы по полученной ссылке, так что это совершенно правильный код.

Container c = new Container();  
Console.WriteLine(c.MyList.Count);  
c.MyList.Add(4);  
Console.WriteLine(c.MyList.Count);  

что нарушает всю концепцию «только для чтения».

Есть ли какой-нибудь разумный обходной путь, который позволил бы мне получить настоящую ссылку только для чтения?

P.S. Я не могу просто вернуть копию списка, потому что тогда пользователь будет думать, что он внес все необходимые изменения, но, увы ... они исчезнут.


person SWeko    schedule 14.01.2009    source источник
comment
Вы забыли написать в своем коде, что _myList доступен только для чтения. ;)   -  person Spoike    schedule 14.01.2009
comment
Поле само по себе не предназначено только для чтения, доступ к нему осуществляется из других методов класса, только свойство доступно только для чтения. Однако даже объявление поля доступным только для чтения не помогло :(   -  person SWeko    schedule 14.01.2009
comment
Когда я пробую, он складывается идеально. если у вас есть поле только для чтения, это поле может быть назначено один раз (статически или в конструкторе).   -  person Spoike    schedule 14.01.2009


Ответы (5)


Ссылка - "только для чтения", реальный объект. Т.е. вы не можете заменить ссылку другим объектом. Итак, если у вас есть класс, который разбивает его следующим образом:

public class Container
{
    private readonly  List<int> _myList;

    public List<int> MyList
    {
        get { return _myList;}
    }

    public Container() : base ()
    {
        _myList = new List<int>();
    }

    public void BreakReadOnly()
    {
        _myList = new List<int>();
    }
}

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

Если вам действительно нужна его коллекция только для чтения, вы можете сделать это следующим образом:

    public ReadOnlyCollection<int> MyList
    {
        get { return _myList.AsReadOnly(); }
    }

Надеюсь это поможет.

Обновлено: больше не используется IEnumerable.

person Spoike    schedule 14.01.2009
comment
Нет проблем с методами класса, обращающимися к частному полю, я просто хочу, чтобы внешний мир не вмешивался в него, поэтому использование только для чтения в поле только помешало бы мне (я добавил SomeMethod к вопросу) - person SWeko; 14.01.2009
comment
Обновил текст. Но, SWeko, вы хотите, чтобы он был доступен только для чтения, так как вы не можете трогать внутренности кем-либо вообще, даже владеющим объектом / классом, у которого он есть? Или просто снаружи? - person Spoike; 14.01.2009
comment
извините, я имел в виду _myList.AsReadOnly (); в реальной проблеме я не могу изменить тип возвращаемого значения (IList ‹›), поэтому теперь я получаю ошибку времени выполнения при добавлении и удалении, но этого более чем достаточно по сравнению с тихими ошибками, которые я получал раньше - person SWeko; 14.01.2009
comment
Очевидно, что примеры Enumerable не справляются со своей задачей, потому что вы всегда можете передать их в List ‹int› и изменить содержимое. Пожалуйста, исправьте это. - person bruno conde; 14.01.2009

Не возвращайте прямую ссылку на ваш список. Вместо этого верните ReadOnlyCollection, обернутую вокруг него, или, если тип возврата List ‹> установлен в камне, верните копию вашего списка. Они могут делать с копией все, что захотят, не затрагивая оригинал.

person GWLlosa    schedule 14.01.2009

Вы можете вернуть коллекцию только для чтения следующим образом:

    public ReadOnlyCollection<int> MyList
    {
        get { return _myList.AsReadOnly(); }
    }

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

    public List<int> MyList
    {
        get { return new List<int>(_myList)}
    }
person bruno conde    schedule 14.01.2009

Взгляните на статический метод Array.AsReadOnly (), который вы можете использовать для возврата оболочки вокруг массива, которая предотвращает его изменение.

person Stu Mackellar    schedule 14.01.2009

Вы хотите, чтобы _myList, ссылочный тип, был доступен только для чтения. Что ж, это только чтение, и нет отказа от концепции только для чтения.

CLR реализует свойство readonly для ссылочных типов таким образом, что ссылка (указатель на объект, если хотите) - это то, что делается только для чтения, в то время как объект, на который указывает ссылка, может быть изменен.

Чтобы обойти это, вам нужно сделать сами поля-члены объекта только для чтения, потому что вы не можете сделать весь объект только для чтения, прикрепив флаг только для чтения к ссылке на объект.

Вы можете реализовать свой неизменяемый универсальный класс коллекции, который будет вести себя так же, как объект List ‹>, но с членами только для чтения.

person Lonzo    schedule 14.01.2009