Я пытаюсь понять, какие варианты использования потребуют от меня объявления List <string> как типа ReadOnly.
Связанный с этим вопрос: сколько памяти при создании экземпляра списка распределяется?
Я пытаюсь понять, какие варианты использования потребуют от меня объявления List <string> как типа ReadOnly.
Связанный с этим вопрос: сколько памяти при создании экземпляра списка распределяется?
Основная причина пометить поле как readonly
так, чтобы вы знали, что обычный код не может заменить ссылку на список. Один из ключевых сценариев, когда это имеет значение, - это если у вас есть другой код в типе, который выполняет синхронизацию с списком с помощью lock(theListField)
. Очевидно, если кто-то поменяет экземпляр списка: все сломается. Обратите внимание, что в большинстве типов, у которых есть список/коллекция, не ожидается изменения экземпляра, поэтому этот readonly
утверждает это ожидание. Общий шаблон:
private List<Foo> _items = new List<Foo>();
public List<Foo> Items => _items;
или
public List<Foo> Items {get;} = new List<Foo>();
В первом примере это должно быть прекрасно, чтобы отметить это поле как readonly
:
private readonly List<Foo> _items = new List<Foo>();
Маркировка поля как readonly
не влияет на распределения и т.д. Он также не делает список доступным только для чтения: просто поле. Вы все еще можете Add()
/Remove()
/Clear()
и т.д. Единственное, что вы не можете сделать, это изменить экземпляр списка как совершенно другой экземпляр списка; вы, конечно, можете полностью изменить содержимое. И только чтение - ложь: отражение и небезопасный код могут изменять значение поля readonly
.
Существует один сценарий, в котором readonly
может иметь отрицательное воздействие и относится к большим полям struct
и вызовам на них. Если поле readonly
, компилятор копирует структуру в стек перед вызовом метода, а не выполняет метод на месте в поле; ldfld
+ stloc
+ ldloca
(если поле readonly
) vs ldflda
(если оно не отмечено readonly
); это связано с тем, что компилятор не может доверять методу не изменять значение. Он даже не может проверить, все ли в структуре readonly
, потому что этого недостаточно: метод struct
может переписать this
:
struct EvilStruct
{
readonly int _id;
public EvilStruct(int id) { _id = id; }
public void EvilMethod() { this = new EvilStruct(_id + 1); }
}
Поскольку компилятор пытается обеспечить природу поля readonly
, если у вас есть:
readonly EvilStruct _foo;
//...
_foo.EvilMethod();
он хочет убедиться, что EvilMethod()
не может перезаписать _foo
новым значением. Отсюда гимнастика и копия в стеке. Обычно это оказывает незначительное влияние, но если структура атипично велика, это может вызвать проблемы с производительностью. Та же проблема обеспечения того, что значение не изменяется, также относится к новому модификатору аргумента in
в С# 7.2:
void(in EvilStruct value) {...}
когда вызывающий абонент хочет гарантировать, что он не изменит значение (это фактически ref EvilStruct
, поэтому изменения будут распространяться).
Эта проблема разрешена в С# 7.2 добавлением синтаксиса readonly struct
- это говорит компилятору, что безопасно вызывать метод in-situ без необходимости делать дополнительную копию стека:
readonly struct EvilStruct
{
readonly int _id;
public EvilStruct(int id) { _id = id; }
// the following method no longer compiles:
// CS1604 Cannot assign to 'this' because it is read-only
public void EvilMethod() { this = new EvilStruct(_id + 1); }
}
Весь этот сценарий не относится к List<T>
, потому что это ссылочный тип, а не тип значения.
С помощью readonly вы можете установить значение поля либо в объявление или в конструкторе объекта, что поле является член.
В соответствии с List
это означает, что только ссылка на объект List
будет неизменной (не внутренние строки). U может использовать readonly
для List
только для того, чтобы убедиться, что ссылка на поле не будет переопределена.
вот пример Почему Microsoft рекомендует использовать поля readonly с изменяемыми значениями?
Из всех классов, с которыми вы можете работать в .NET, Strings имеют значительно странное поведение. Строка предназначена во многих случаях работать как тип значения, а не ссылочный тип. И это до того, как мы добавим в состав строки такие строки, как string-interning:
Сравнение значений для равенства в .NET: идентификация и эквивалентность
Все сказанное, я не знаю, почему кто-то отметит список <string> readonly, больше, чем список [int] или список [object].
Сколько выделено памяти: это непредсказуемо. Список будет увеличиваться по мере добавления элементов к нему. И он будет зависеть, чтобы избежать чрезмерного перераспределения. Но точный алгоритм для этого - детальная информация о структуре/классе. Если вы знаете, сколько предметов вам нужно заблаговременно, либо укажите счетчик в конструкторе, либо просто создайте список из вашей статической исходной коллекции (например, массива). Это может быть минимальным увеличением производительности и, как правило, является хорошей практикой. В то же время, интернирование строк будет пытаться ограничить, сколько памяти выделено фактическим строковым экземплярам в памяти, путем повторного использования ссылок.