在 C# 9 记录上使用“with”时忽略特定字段?

And*_*ndi 10 c# c#-9.0 c#-record-type

record使用with关键字创建 C# 9 的新实例时,我想忽略某些字段,而不是将它们也复制到新实例中。

在以下示例中,我有一个Hash属性。因为它在计算上非常昂贵,所以它只在需要时才计算然后缓存(我有一个非常不可变的记录,所以对于一个实例,哈希永远不会改变)。

public record MyRecord {

   // All truely immutable properties
   public int ThisAndManyMoreComplicatedProperties { get; init; } 
   // ...

   // Compute only when required, but then cache it
   public string Hash {
      get {
         if (hash == null)
            hash = ComputeHash();
         return hash;
      }
   }

   private string? hash = null;
}
Run Code Online (Sandbox Code Playgroud)

打电话时

MyRecord myRecord = ...;
var changedRecord = myRecord with { AnyProp = ... };
Run Code Online (Sandbox Code Playgroud)

changedRecord包含hash值 from myRecord,但我想要的null又是默认值。

是否有机会将该hash字段标记为“瞬态”/“内部”/“真正私有”...,或者我是否必须编写自己的复制构造函数来模仿此功能?

Hei*_*nzi 3

我找到了一种解决方法:您可以(ab)使用继承将复制构造函数分成两部分:手动部分仅用于hash(在基类中),而自动生成部分在派生类中复制所有有价值的数据字段。

这具有抽象出散列(非)缓存逻辑的额外优点。这是一个最小的例子(fiddle):

abstract record HashableRecord
{
    protected string hash;
    protected abstract string CalculateHash();
    
    public string Hash 
    {
        get
        {
            if (hash == null)
            {
                hash = CalculateHash(); // do expensive stuff here
                Console.WriteLine($"Calculating hash {hash}");
            }
            return hash;
        }
    }
    
    // Empty copy constructor, because we explicitly *don't* want
    // to copy hash.
    public HashableRecord(HashableRecord other) { }
}

record Data : HashableRecord
{
    public string Value1 { get; init; }
    public string Value2 { get; init; }

    protected override string CalculateHash() 
        => hash = Value1 + Value2; // do expensive stuff here
}

public static void Main()
{
    var a = new Data { Value1 = "A", Value2 = "A" };
    
    // outputs:
    // Calculating hash AA
    // AA
    Console.WriteLine(a.Hash);

    var b = a with { Value2 = "B" };
    
    // outputs:
    // AA
    // Calculating hash AB
    // AB
    Console.WriteLine(a.Hash);
    Console.WriteLine(b.Hash);
}
Run Code Online (Sandbox Code Playgroud)