Подтвердить что ты не робот

Возможны ли неизменные массивы в .NET?

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

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

Я думал, что ключевое слово readonly может сделать трюк, но такой удачи нет.

4b9b3361

Ответ 1

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

Ответ 2

Framework Design Guidelines предлагает вернуть копию массива. Таким образом, потребители не могут изменять элементы из массива.

// 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();
}

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

Ответ 4

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

Ответ 5

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

Для получения дополнительной информации см. Массивы, считающиеся несколько вредными.

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

Ответ 6

.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' }

Ответ 7

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

Ответ 8

В дополнение к ответу Мэт, IList представляет собой полный абстрактный интерфейс для массива, поэтому он позволяет добавлять, удалять и т.д. Я не уверен, почему Lippert, похоже, предлагает его в качестве альтернативы 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, должно само по себе быть неизменным (например, строка), или быть копией, которую вы делаете "на лету".

Ответ 9

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

Ответ 10

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