假设我们有以下只读记录结构定义
public readonly record struct S(int A, int B)
{
}
访问
A
和 B
将导致防御性副本,因为在记录中位置参数将自动实现为属性。
我们可以通过像这样修改定义来避免它
public readonly record struct S(int A, int B)
{
public readonly int A = A;
public readonly int B = B;
}
在这种情况下,
A
和B
是字段,因此对访问没有副作用,因此没有防御副本。根据我对这两个定义的测试和理解,编译器合成了相同的记录特定代码(相等、哈希码生成等)。
我想知道是否有更好的方法来避免带有记录的防御性副本。所有只读字段的定义可能很麻烦并且可能容易出错。是否有任何 C# 语法可以实现我在这里的目标,或者只读字段是我能做的最好的吗?
如果我对本例中的防御副本和记录代码合成的理解是错误的,请解释我哪里不正确。
这里没有防御副本;只读记录被标记为
[IsReadOnly]
,仅获取属性也是如此 - 这足以告诉编译器它们不需要;您可以在 IL 中看到这一点,例如,如果我们这样做:
using System;
void NoCopies(in S obj)
{ // no copies because the *type* is [IsReadOnly]
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
void AlsoHasNoCopies(in T obj)
{ // no copies because the *members* are [IsReadOnly]
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
void StillNoCopies(in LazyManual obj)
{ // no copies because the *members* are [IsReadOnly]
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
void DoesHaveCopies(in BasicManual obj)
{ // no copies because the *members* are [IsReadOnly]
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
public readonly record struct S(int A, int B)
{
}
public record struct T(int A, int B) // not marked readonly
{
}
public struct LazyManual
{
public int A {get;} // auto-marked [IsReadOnly]
public int B {get;}
}
public struct BasicManual
{
private int _a, _b;
public int A => _a; // not auto-marked [IsReadOnly]
public int B => _b;
}
编译/反编译这个,我们看到:
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
}
[CompilerGenerated]
internal static void <<Main>$>g__NoCopies|0_0([In][IsReadOnly] ref S obj)
{
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
[CompilerGenerated]
internal static void <<Main>$>g__AlsoHasNoCopies|0_1([In][IsReadOnly] ref T obj)
{
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
[CompilerGenerated]
internal static void <<Main>$>g__StillNoCopies|0_2([In][IsReadOnly] ref LazyManual obj)
{
Console.WriteLine(obj.A);
Console.WriteLine(obj.B);
}
[CompilerGenerated]
internal static void <<Main>$>g__DoesHaveCopies|0_3([In][IsReadOnly] ref BasicManual obj)
{
BasicManual basicManual = obj;
Console.WriteLine(basicManual.A);
basicManual = obj;
Console.WriteLine(basicManual.B);
}
}
只有手动实现类型的最后一个选项是 not
readonly
并使用手动属性:具有防御性副本。