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>,因为这是引用类型,而不是值类型.
| 归档时间: |
|
| 查看次数: |
2532 次 |
| 最近记录: |