C# 是';只读';修改器是否创建字段的隐藏副本?
C# 是';只读';修改器是否创建字段的隐藏副本?,c#,struct,value-type,readonly-attribute,C#,Struct,Value Type,Readonly Attribute,MutableSlab和immutablelab实现之间的唯一区别是readonly修饰符应用于句柄字段: 使用系统; 使用System.Runtime.InteropServices; 公共课程 { 类MutableSlab:IDisposable { 私人GCHandle; 公共可变板() { this.handle=GCHandle.Alloc(新字节[256],GCHandleType.pinted); } public bool IsAllocated=>this.handle.IsA
MutableSlab
和immutablelab
实现之间的唯一区别是readonly
修饰符应用于句柄
字段:
使用系统;
使用System.Runtime.InteropServices;
公共课程
{
类MutableSlab:IDisposable
{
私人GCHandle;
公共可变板()
{
this.handle=GCHandle.Alloc(新字节[256],GCHandleType.pinted);
}
public bool IsAllocated=>this.handle.IsAllocated;
公共空间处置()
{
this.handle.Free();
}
}
类ImmutableLab:IDisposable
{
私有只读GCHandle句柄;
公共ImmutableLab()
{
this.handle=GCHandle.Alloc(新字节[256],GCHandleType.pinted);
}
public bool IsAllocated=>this.handle.IsAllocated;
公共空间处置()
{
this.handle.Free();
}
}
公共静态void Main()
{
var mutableSlab=新的mutableSlab();
var ImmutableLab=新的ImmutableLab();
mutableSlab.Dispose();
immutableLab.Dispose();
Console.WriteLine($“{nameof(mutableSlab)}.handle.IsAllocated={mutableSlab.IsAllocated}”);
Console.WriteLine($“{nameof(ImmutableLab)}.handle.IsAllocated={ImmutableLab.IsAllocated}”);
}
}
但它们会产生不同的结果:
mutableSlab.handle.IsAllocated = False
immutableSlab.handle.IsAllocated = True
GCHandle是一个可变结构,当您复制它时,它的行为与ImmutableLab
场景中的行为完全相同
readonly
修饰符是否创建字段的隐藏副本?这是否意味着它不仅仅是编译时检查?我找不到关于这种行为的任何信息。这种行为有记录吗
readonly
修饰符是否创建字段的隐藏副本
在常规结构类型(构造函数或静态构造函数之外)的只读字段上调用方法或属性首先复制该字段,是的。这是因为编译器不知道属性或方法访问是否会修改您调用它的值
从:
第12.7.5.1节(会员访问,一般)
这将对成员访问进行分类,包括:
- 如果我识别一个静态字段:
- 如果该字段是只读的,并且引用发生在声明该字段的类或结构的静态构造函数之外,则结果是一个值,即E中静态字段I的值
- 否则,结果是一个变量,即E中的静态场I
- 如果T是结构类型,并且我标识了该结构类型的实例字段:
- 如果E是一个值,或者如果该字段是只读的,并且引用发生在声明该字段的结构的实例构造函数之外,则结果是一个值,即E给出的结构实例中的字段I的值
- 否则,结果是一个变量,即E给出的struct实例中的字段I
- 否则,如果E的类型是值类型V,并且M在V中声明或重写:
- [……]
- 如果E未分类为变量,则会创建E类型的临时局部变量,并将E的值指定给该变量。然后将E重新分类为该临时局部变量的参考。临时变量在M中是可以访问的,但不能以任何其他方式访问。因此,只有当E是真变量时,调用者才可能观察到M对此所做的更改
使用系统;
利用制度全球化;
结构计数器
{
私人整数计数;
公共整数递增计数=>++计数;
}
课堂测试
{
静态只读计数器只读计数器;
静态计数器读写计数器;
静态void Main()
{
Console.WriteLine(readOnlyCounter.IncrementedCount);//1
Console.WriteLine(readOnlyCounter.IncrementedCount);//1
Console.WriteLine(readOnlyCounter.IncrementedCount);//1
Console.WriteLine(readWriteCounter.IncrementedCount);//1
Console.WriteLine(readWriteCounter.IncrementedCount);//2
Console.WriteLine(readWriteCounter.IncrementedCount);//3
}
}
这是调用readOnlyCounter.IncrementedCount
的IL:
ldsfld valuetype Counter Test::readOnlyCounter
stloc.0
ldloca.s V_0
call instance int32 Counter::get_IncrementedCount()
将字段值复制到堆栈上,然后调用属性。。。因此字段的值最终不会改变;它在副本中递增count
将其与读写字段的IL进行比较:
ldsflda valuetype Counter Test::readWriteCounter
call instance int32 Counter::get_IncrementedCount()
这使得直接对字段进行调用,因此字段值最终会在属性中发生更改
当结构较大且成员未对其进行变异时,创建副本可能效率低下。这就是为什么在C#7.2及更高版本中,readonly
修饰符可以应用于结构。下面是另一个例子:
使用系统;
利用制度全球化;
只读结构ReadOnlyStruct
{
公共void NoOp(){}
}
课堂测试
{
静态只读只读结构字段1;
静态只读结构字段2;
静态void Main()
{
字段1.NoOp();
字段2.NoOp();
}
}
使用<代码