Вопрос

Можно ли как-то отметить System.Array как неизменяемый.Когда они помещены за public-get/private-set, их нельзя добавить, поскольку это требует перераспределения и переназначения, но потребитель все равно может установить любой индекс, который пожелает:

public class Immy
{
    public string[] { get; private set; }
}

Я думал, readonly Ключевое слово могло бы помочь, но не тут-то было.

Это было полезно?

Решение

ReadOnlyCollection<T> вероятно, это то, что вы ищете.У него нет Add() метод.

Другие советы

А Рекомендации по проектированию каркаса предложите вернуть копию массива.Таким образом, потребители не смогут изменять элементы массива.

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}

эти лучше:

public static ReadOnlyCollection<char> GetInvalidPathChars(){
   return Array.AsReadOnly(InvalidPathChars);
}

public static char[] GetInvalidPathChars(){
   return (char[])InvalidPathChars.Clone();
}

Примеры взяты прямо из книги.

Пожалуйста, посмотри Неизменяемые коллекции теперь доступны в библиотеке базовых классов (в настоящее время в предварительной версии).

Вы можете использовать метод Array.AsReadOnly для возврата.

Я считаю, что лучше всего использовать IList<T>, а не массивы в общедоступных API именно по этой причине. только для чтения предотвратит установку переменной-члена вне конструктора, но, как вы обнаружили, не помешает людям назначать элементы в массиве.

Видеть Массивы считаются несколько вредными Чтобы получить больше информации.

Редактировать: Массивы не могут быть доступны только для чтения, но их можно преобразовать в реализации IList только для чтения с помощью Array.AsReadOnly(), как указывает @shahkalpesh.

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

Если вы хотите пометить набор данных как неизменяемый, вы выходите за рамки возможностей, предоставляемых традиционным массивом..NET предоставляет эквивалентные возможности, но технически не в форме массива.Чтобы получить неизменяемую коллекцию из массива, используйте Array.AsReadOnly<T>:

var mutable = new[]
{
    'a', 'A',
    'b', 'B',
    'c', 'C',
};

var immutable = Array.AsReadOnly(mutable);

immutable будет ReadOnlyCollection<char> пример.В качестве более общего варианта использования вы можете создать ReadOnlyCollection<T> из любого общего IList<T> выполнение.

var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));

Обратите внимание, что это должна быть общая реализация;простой старый IList не будет работать, а это означает, что вы не можете использовать этот метод в традиционном массиве, который реализует только IList.Это выявляет возможность использования Array.AsReadOnly<T> как быстрый способ получения доступа к универсальным реализациям, которые обычно недоступны через традиционный массив.

ReadOnlyCollection<T> предоставит вам доступ ко всем функциям, которые вы ожидаете от неизменяемого массива:

// Note that .NET favors Count over Length; all but traditional arrays use Count:
for (var i = 0; i < immutable.Count; i++)
{
    // this[] { get } is present, as ReadOnlyCollection<T> implements IList<T>:
    var element = immutable[i]; // Works

    // this[] { set } has to be present, as it is required by IList<T>, but it
    // will throw a NotSupportedException:
    immutable[i] = element; // Exception!
}

// ReadOnlyCollection<T> implements IEnumerable<T>, of course:
foreach (var character in immutable)
{
}

// LINQ works fine; idem
var lowercase =
    from c in immutable
    where c >= 'a' && c <= 'z'
    select c;

// You can always evaluate IEnumerable<T> implementations to arrays with LINQ:
var mutableCopy = immutable.ToArray();
// mutableCopy is: new[] { 'a', 'A', 'b', 'B', 'c', 'C' }
var lowercaseArray = lowercase.ToArray();
// lowercaseArray is: new[] { 'a', 'b', 'c' }

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

В дополнение к ответу Мэтта, IList — это полный абстрактный интерфейс к массиву, поэтому он позволяет добавлять, удалять и т. д.Я не уверен, почему Липперт предлагает его в качестве альтернативы IEnumerable, где необходима неизменность.(Редактировать: потому что реализация IList может генерировать исключения для этих мутирующих методов, если вам нравятся такие вещи).

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

Убедитесь, что элементы в списке неизменяемы (как в вашем примере:строка неизменяема).

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

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

interface IImmutable
{
    public string ValuableCustomerData { get; }
}

class Mutable, IImmutable
{
    public string ValuableCustomerData { get; set; }
}

public class Immy
{
    private List<Mutable> _mutableList = new List<Mutable>();

    public IEnumerable<IImmutable> ImmutableItems
    {
        get { return _mutableList.Cast<IMutable>(); }
    }
}

Обратите внимание, что каждое значение, доступное из интерфейса IImmutable, само должно быть неизменяемым (например,строка), или же быть копией, которую вы делаете на лету.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top