«Только для чтения» свойство Accessor в C # - PullRequest
19 голосов
/ 06 марта 2009

У меня есть следующий класс:

class SampleClass
{
   private ArrayList mMyList;

   SampleClass()
   {
       // Initialize mMyList
   }

   public ArrayList MyList
   {
       get { return mMyList;}
   }
}

Я хочу, чтобы пользователи могли получать mMyList, поэтому я выставил «get» через свойство, однако я не хочу, чтобы они вносили изменения в объект (т.е. MyList.Add (new Class ());) вернуться в мой класс.

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

Возможно ли это?

Ответы [ 8 ]

25 голосов
/ 06 марта 2009

С ArrayList вы довольно ограничены, потому что в BCL нет не предназначенного для чтения неуниверсального класса коллекции. Быстрое и грязное решение - вернуть тип IEnumerable.

   public IEnumerable MyList
   {
       get { return mMyList;}
   }

Это на самом деле не помешает кому-либо приводить в ArrayList, но также не разрешит редактирование по умолчанию

Вы можете вернуть список только для чтения, вызвав ArrayList.ReadOnly. Однако тип возвращаемого значения - ArrayList, поэтому пользователь все равно сможет компилировать с .Add, но это приведет к ошибке времени выполнения.

19 голосов
/ 06 марта 2009

Используйте метод ArrayList.ReadOnly () для создания и возврата доступной только для чтения оболочки по списку. он не будет копировать список, а просто создаст оболочку только для чтения. Чтобы получить проверку во время компиляции, вы, вероятно, захотите вернуть оболочку только для чтения, как IEnumerable, как предлагает @Jared.

public IEnumerable MyList
{
     get { return ArrayList.ReadOnly(mMyList); }
}

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

Этот метод является операцией O (1).

Ссылки

9 голосов
/ 06 марта 2009

Просто чтобы расширить ответ JaredPar. По его словам, возвращение фактического поля поддержки не является полностью безопасным, поскольку пользователь все еще может динамически преобразовать IEnumerable обратно в ArrayList.

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

public IEnumerable MyList
{
    get
    {
         foreach (object o in mMyList)
             yield return o;
    }
}

Опять же, я бы, вероятно, также использовал общий список (IEnumerable<T>), чтобы быть полностью безопасным для типов.

4 голосов
/ 06 марта 2009
4 голосов
/ 06 марта 2009

Используйте ReadOnlyCollection.

return new ReadOnlyCollection<object>(mMyList).

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

Если вы знаете, что mMyList содержит только один тип объекта (например, он не имеет ничего, кроме DateTimes), Вы можете вернуть ReadOnlyCollection<DateTime> (или некоторый другой тип) для дополнительной безопасности типа. Если вы используете C # 3, вы можете просто написать

return new ReadOnlyCollection<DateTime>(mMyList.OfType<DateTime>().ToArray())

Однако, это не будет автоматически обновляться с нижележащим списком, а также будет менее эффективным (оно скопирует весь список). Лучший вариант - сделать mMyList универсальным List<T> (например, List<DateTime>)

1 голос
/ 06 марта 2009

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

0 голосов
/ 26 марта 2013

Доступно в .NET v2.0

    public ArrayList MyList { get; private set; }
0 голосов
/ 06 марта 2009

Просто сделайте свойство как Sealed (или NotInheritable в VB.NET) и только для чтения, как это:

   public sealed readonly ArrayList MyList
   {
       get { return mMyList;}
   }

Также не забудьте сделать вспомогательное поле только для чтения:

   private readonly ArrayList mMyList;

Но чтобы инициализировать значение mMyList, вы должны инициализировать его ТОЛЬКО в конструкторе, как в этом примере:

public SampleClass()
{
   // Initialize mMyList
   mMyList = new ArrayList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...