使用用例来理解为什么应该将字符串列表声明为readonly

Viv*_*kla 10 c# clr

我试图了解哪些用例需要我将List <string>声明为ReadOnly类型.

与此相关的问题是:列表实例化时会分配多少内存?

Mar*_*ell 42

将字段标记为的主要原因readonly是,您知道常规代码不能交换列表引用.可能重要的一个关键方案是,如果您在使用a对列表执行同步的类型中有其他代码lock(theListField).显然,如果有人交换列表实例:事情就会破裂.请注意,在大多数具有列表/集合的类型中,预计不会更改实例,因此会readonly断言该期望.一种常见的模式是:

private List<Foo> _items = new List<Foo>();
public List<Foo> Items => _items;
Run Code Online (Sandbox Code Playgroud)

要么:

public List<Foo> Items {get;} = new List<Foo>();
Run Code Online (Sandbox Code Playgroud)

在第一个例子中,将该字段标记为readonly:

private readonly List<Foo> _items = new List<Foo>();
Run Code Online (Sandbox Code Playgroud)

将字段标记为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); }
}
Run Code Online (Sandbox Code Playgroud)

因为编译器试图强制执行readonly字段的性质,如果您有:

readonly EvilStruct _foo;
//...
_foo.EvilMethod();
Run Code Online (Sandbox Code Playgroud)

它希望确保EvilMethod()不能_foo用新值覆盖.因此体操和堆栈上的副本.通常这可以忽略不计,但如果结构非常大,那么这可能会导致性能问题.保证值不会改变的同样问题也适用in于C#7.2中的新参数修饰符:

void(in EvilStruct value) {...}
Run Code Online (Sandbox Code Playgroud)

调用者想要保证它不会改变值(这实际上是a ref EvilStruct,因此会传播更改).

通过添加readonly struct语法在C#7.2中解决了这个问题- 这告诉编译器在不必进行额外堆栈复制的情况下原位调用该方法是安全的:

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); }
}
Run Code Online (Sandbox Code Playgroud)

整个方案不适用List<T>,因为这是引用类型,而不是值类型.

  • 为什么不自动推断`readonly struct`?编译器知道是否有任何方法更改`this`并且不能扩展struct以添加更改`this`的新方法.是否有理由不在可能的情况下标记结构`readonly`? (2认同)
  • @AlexeyRomanov这是一个有趣的场景,涉及两个项目引用彼此,构建顺序和变化使得推理变得脆弱 - 至少如果你明确删除`readonly`你正在做出明确的决定**来违反合同 - 相反意外地违反你*不知道的合同*(通过添加一个改变`this`的方法).它可能会成为一个好的代码分析器,但是 - *建议*当你可以将`struct`变成`readonly struct'时 (2认同)