避免只读记录结构中的防御性副本

问题描述 投票:0回答:1

假设我们有以下只读记录结构定义

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# 语法可以实现我在这里的目标,或者只读字段是我能做的最好的吗?

如果我对本例中的防御副本和记录代码合成的理解是错误的,请解释我哪里不正确。

c# struct properties field defensive-copy
1个回答
0
投票

这里没有防御副本;只读记录被标记为

[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
并使用手动属性:具有防御性副本。

© www.soinside.com 2019 - 2024. All rights reserved.