结论
字段
可以随便调用,无影响- 调用
非readonly属性/方法
时,会产生防御性副本 - 给
属性/方法
加上readonly关键字后,不会产生防御性副本了
编译器无法确定用户的Get属性以及方法里的代码是否有改成员字段,所以使用防御性拷贝保证结果正确性。
readonly ref readonly GetMethod(); readonly在ref之前,才能保证方法里不会有改成员变量,ref之后的readonly则表示ref是只读的。
测试代码
Source
public struct NonReadOnlyStruct
{
public long PublicField;
public long PublicProperty { get; }
public void PublicMethod() { }
public readonly long ReadOnlyPublicProperty { get; }
public readonly void ReadOnlyPublicMethod() { }
private static readonly NonReadOnlyStruct _ros = new();
static void nrs_PublicField(in NonReadOnlyStruct nrs)
{
// Ok. Public field access causes no hidden copies
var x = nrs.PublicField;
}
static void ros_PublicField(in NonReadOnlyStruct nrs)
{
// Ok. No hidden copies.
var x = _ros.PublicField;
}
static void nrs_ReadOnlyPublicProperty(in NonReadOnlyStruct nrs)
{
// Ok. No hidden copies
var x = nrs.ReadOnlyPublicProperty;
}
static void nrs_PublicProperty(in NonReadOnlyStruct nrs)
{
// Hidden copy: Property access on 'in'-parameter
var x = nrs.PublicProperty;
}
static void nrs_ReadOnlyPublicMethod(in NonReadOnlyStruct nrs)
{
// Ok. No hidden copies
nrs.ReadOnlyPublicMethod();
}
static void nrs_PublicMethod(in NonReadOnlyStruct nrs)
{
// Hidden copy: method call on non readonly
nrs.PublicMethod();
}
static void ros_PublicMethod(in NonReadOnlyStruct nrs)
{
// Hidden copy: Method call on readonly field
_ros.PublicMethod();
}
static void LocalRef_PublicMethod(in NonReadOnlyStruct nrs)
{
// Hidden copy: method call on ref readonly local
ref readonly var local = ref nrs;
local.PublicMethod();
}
static void LocalFunc_PublicMethod(in NonReadOnlyStruct nrs)
{
// Hidden copy: method call on ref readonly return
Local().PublicMethod();
ref readonly NonReadOnlyStruct Local() => ref _ros;
}
static void LocalRef_ReadOnlyPublicMethod(in NonReadOnlyStruct nrs)
{
// Ok. No hidden copies
ref readonly var local = ref nrs;
local.ReadOnlyPublicMethod();
}
static void LocalFunc_ReadOnlyPublicMethod(in NonReadOnlyStruct nrs)
{
// Ok. No hidden copies
Local().ReadOnlyPublicMethod();
ref readonly NonReadOnlyStruct Local() => ref _ros;
}
}
IL
// Methods
.method public hidebysig specialname
instance int64 get_PublicProperty () cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
.custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a35
// Header size: 1
// Code size: 7 (0x7)
.maxstack 8
// return <PublicProperty>k__BackingField;
IL_0000: ldarg.0
IL_0001: ldfld int64 NonReadOnlyStruct::'<PublicProperty>k__BackingField'
IL_0006: ret
} // end of method NonReadOnlyStruct::get_PublicProperty
.method public hidebysig
instance void PublicMethod () cil managed
{
// Method begins at RVA 0x5a3d
// Header size: 1
// Code size: 1 (0x1)
.maxstack 8
// }
IL_0000: ret
} // end of method NonReadOnlyStruct::PublicMethod
.method public hidebysig specialname
instance int64 get_ReadOnlyPublicProperty () cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
.custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a3f
// Header size: 1
// Code size: 7 (0x7)
.maxstack 8
// return <ReadOnlyPublicProperty>k__BackingField;
IL_0000: ldarg.0
IL_0001: ldfld int64 NonReadOnlyStruct::'<ReadOnlyPublicProperty>k__BackingField'
IL_0006: ret
} // end of method NonReadOnlyStruct::get_ReadOnlyPublicProperty
.method public hidebysig
instance void ReadOnlyPublicMethod () cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a47
// Header size: 1
// Code size: 1 (0x1)
.maxstack 8
// }
IL_0000: ret
} // end of method NonReadOnlyStruct::ReadOnlyPublicMethod
.method private hidebysig static
void nrs_PublicField (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a49
// Header size: 1
// Code size: 1 (0x1)
.maxstack 8
// }
IL_0000: ret
} // end of method NonReadOnlyStruct::nrs_PublicField
.method private hidebysig static
void ros_PublicField (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a4b
// Header size: 1
// Code size: 7 (0x7)
.maxstack 8
// _ = _ros;
IL_0000: ldsfld valuetype NonReadOnlyStruct NonReadOnlyStruct::_ros
IL_0005: pop
// }
IL_0006: ret
} // end of method NonReadOnlyStruct::ros_PublicField
.method private hidebysig static
void nrs_ReadOnlyPublicProperty (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a53
// Header size: 1
// Code size: 8 (0x8)
.maxstack 8
// _ = nrs.ReadOnlyPublicProperty;
IL_0000: ldarg.0
IL_0001: call instance int64 NonReadOnlyStruct::get_ReadOnlyPublicProperty()
IL_0006: pop
// }
IL_0007: ret
} // end of method NonReadOnlyStruct::nrs_ReadOnlyPublicProperty
.method private hidebysig static
void nrs_PublicProperty (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a5c
// Header size: 1
// Code size: 8 (0x8)
.maxstack 8
// _ = nrs.PublicProperty;
IL_0000: ldarg.0
IL_0001: call instance int64 NonReadOnlyStruct::get_PublicProperty()
IL_0006: pop
// }
IL_0007: ret
} // end of method NonReadOnlyStruct::nrs_PublicProperty
.method private hidebysig static
void nrs_ReadOnlyPublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a65
// Header size: 1
// Code size: 7 (0x7)
.maxstack 8
// nrs.ReadOnlyPublicMethod();
IL_0000: ldarg.0
IL_0001: call instance void NonReadOnlyStruct::ReadOnlyPublicMethod()
// }
IL_0006: ret
} // end of method NonReadOnlyStruct::nrs_ReadOnlyPublicMethod
.method private hidebysig static
void nrs_PublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a70
// Header size: 12
// Code size: 15 (0xf)
.maxstack 1
.locals init (
[0] valuetype NonReadOnlyStruct
)
// nrs.PublicMethod();
IL_0000: ldarg.0
IL_0001: ldobj NonReadOnlyStruct
IL_0006: stloc.0
// (no C# code)
IL_0007: ldloca.s 0
// }
IL_0009: call instance void NonReadOnlyStruct::PublicMethod()
IL_000e: ret
} // end of method NonReadOnlyStruct::nrs_PublicMethod
.method private hidebysig static
void ros_PublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5a8c
// Header size: 12
// Code size: 14 (0xe)
.maxstack 1
.locals init (
[0] valuetype NonReadOnlyStruct
)
// _ros.PublicMethod();
IL_0000: ldsfld valuetype NonReadOnlyStruct NonReadOnlyStruct::_ros
IL_0005: stloc.0
// (no C# code)
IL_0006: ldloca.s 0
// }
IL_0008: call instance void NonReadOnlyStruct::PublicMethod()
IL_000d: ret
} // end of method NonReadOnlyStruct::ros_PublicMethod
.method private hidebysig static
void LocalRef_PublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5aa8
// Header size: 12
// Code size: 15 (0xf)
.maxstack 1
.locals init (
[0] valuetype NonReadOnlyStruct
)
// nrs.PublicMethod();
IL_0000: ldarg.0
IL_0001: ldobj NonReadOnlyStruct
IL_0006: stloc.0
// (no C# code)
IL_0007: ldloca.s 0
// }
IL_0009: call instance void NonReadOnlyStruct::PublicMethod()
IL_000e: ret
} // end of method NonReadOnlyStruct::LocalRef_PublicMethod
.method private hidebysig static
void LocalFunc_PublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x5ac4
// Header size: 12
// Code size: 19 (0x13)
.maxstack 1
.locals init (
[0] valuetype NonReadOnlyStruct
)
// Local().PublicMethod();
IL_0000: call valuetype NonReadOnlyStruct& NonReadOnlyStruct::'<LocalFunc_PublicMethod>g__Local|18_0'()
IL_0005: ldobj NonReadOnlyStruct
IL_000a: stloc.0
// (no C# code)
IL_000b: ldloca.s 0
// }
IL_000d: call instance void NonReadOnlyStruct::PublicMethod()
IL_0012: ret
} // end of method NonReadOnlyStruct::LocalFunc_PublicMethod
.method private hidebysig static
void LocalRef_ReadOnlyPublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x265b
// Header size: 1
// Code size: 7 (0x7)
.maxstack 8
// nrs.ReadOnlyPublicMethod();
IL_0000: ldarg.0
IL_0001: call instance void NonReadOnlyStruct::ReadOnlyPublicMethod()
// }
IL_0006: ret
} // end of method NonReadOnlyStruct::LocalRef_ReadOnlyPublicMethod
.method private hidebysig static
void LocalFunc_ReadOnlyPublicMethod (
[in] valuetype NonReadOnlyStruct& nrs
) cil managed
{
.param [1]
.custom instance void [netstandard]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2663
// Header size: 1
// Code size: 11 (0xb)
.maxstack 8
// Local().ReadOnlyPublicMethod();
IL_0000: call valuetype NonReadOnlyStruct& NonReadOnlyStruct::'<LocalFunc_ReadOnlyPublicMethod>g__Local|20_0'()
IL_0005: call instance void NonReadOnlyStruct::ReadOnlyPublicMethod()
// }
IL_000a: ret
} // end of method NonReadOnlyStruct::LocalFunc_ReadOnlyPublicMethod
参考
https://docs.microsoft.com/zh-cn/dotnet/csharp/write-safe-efficient-code