Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用“时忽略特定字段”;加上;在C#9记录上?_C#_C# 9.0_C# Record Type - Fatal编程技术网

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

使用“时忽略特定字段”;加上;在C#9记录上?,c#,c#-9.0,c#-record-type,C#,C# 9.0,C# Record Type,使用with关键字创建C#9记录的新实例时,我希望忽略一些字段,而不是将它们复制到新实例中 在下面的示例中,我有一个Hash属性。因为它在计算上非常昂贵,所以它只在需要时计算,然后缓存(我有一个非常不可变的记录,所以哈希值对于一个实例永远不会改变) 公共记录MyRecord{ //所有真正不变的属性 public int thisandmanymorecomplicated属性{get;init;} // ... //仅在需要时计算,然后缓存它 公共字符串散列{ 得到{ if(hash==nul

使用
with
关键字创建C#9
记录的新实例时,我希望忽略一些字段,而不是将它们复制到新实例中

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

公共记录MyRecord{
//所有真正不变的属性
public int thisandmanymorecomplicated属性{get;init;}
// ...
//仅在需要时计算,然后缓存它
公共字符串散列{
得到{
if(hash==null)
hash=ComputeHash();
返回散列;
}
}
私有字符串?散列=null;
}
打电话的时候

MyRecord MyRecord=。。。;
var changedRecord=myRecord,带有{AnyProp=…};
changedRecord
包含
myRecord
中的
hash
值,但我想要的是默认值
null


是否有机会将
散列
字段标记为“transient”/“internal”/“reallyprivate”…,或者我必须编写自己的复制构造函数来模拟此功能?

如果我正确理解您的意思,您希望创建一个具有现有MyRecord对象某些属性的新MyRecord对象

我认为应该采取以下措施:

MyRecord myRecord = ...;
var changedRecord = new MyRecord with { AnyProp = myRecord.AnyProp... };
正如您在反编译中所看到的那样,
with
调用被转换为
$()
方法调用,该方法调用在内部调用编译器生成的复制构造函数,因此您需要定义自己的方法来防止调用
散列

同样,带有
关键字的
说明:

如果需要自定义记录复制语义,请显式声明具有所需行为的复制构造函数


我认为唯一允许这样做的内置机制是“复制构造函数”。如以下文件所述:

记录隐式定义了一个受保护的“复制构造函数”—一个构造函数,它接受现有的记录对象,并将其逐字段复制到新的记录对象

“复制构造函数”只是一个构造函数,它接收与参数类型相同的记录实例。如果只实现此构造函数,则可以使用
表达式覆盖
的默认行为。我已经根据您的代码进行了测试,下面是记录声明:

公共记录MyRecord
{
受保护的MyRecord(MyRecord原件)
{
this和maymorecomplementdproperties=原始。this和maymorecomplementdproperties;
hash=null;
}
public int this和maymore使属性{get;init;}
string?hash=null;
公共字符串散列
{
得到
{
if(散列为空)
{
WriteLine(“存储的哈希当前为空。”);
}
返回hash???=ComputeHash();
}
}
字符串ComputeHash()=>“”.PadLeft(100,'A');
}
请注意,当我调用属性getter时,我会检查
哈希值是否为null并打印一条消息。然后我做了一个小程序来检查:

var-record=new-MyRecord{thisandmaydromedproperties=100};
Console.WriteLine($“{record.Hash}”);
var newRecord=具有{thisandmaymoredproperties=200}的记录;
Console.WriteLine($“{newRecord.Hash}”);
如果运行此命令,您会注意到对
Hash
的两个调用都将打印私有
Hash
为空的消息。如果对复制构造函数进行注释,您将看到只有第一个调用打印空值


所以我认为这解决了你的问题。这种方法的缺点是,您必须手动复制记录的每个属性,这可能非常烦人。如果您的记录有很多属性,您可以使用反射来迭代,然后只复制您想要的属性。您还可以定义自定义
属性
,以标记忽略字段。但请记住,使用反射总是有处理开销。

我找到了解决问题的方法。这并不能解决一般问题,它还有另一个缺点:我必须缓存对象的最后一个状态,直到重新计算哈希为止。我知道这是一个潜在的繁重的计算和更高的内存使用之间的折衷

诀窍是在计算哈希时记住最后一个对象引用。再次调用
Hash
属性时,我会检查对象引用是否已更改(即是否创建了新对象)

公共字符串哈希{
得到{
if(hash==null | | false==ReferenceEquals(this,hashRef)){
hash=ComputeHash();
hashRef=这个;
}
返回散列;
}
}
私人字符串?hash=null;
私人档案?hashRef=null;
我仍在寻找更好的解决方案


编辑:我推荐

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

这还有一个额外的优点,即抽象掉散列(非)缓存逻辑。下面是一个最小的示例():


是的,我非常确定没有内置的解决方案,这是这个用例的预期解决方案。我有点不太理解你的要求。。当
使用
克隆记录时,新记录将获得现有哈希值(它是一个克隆,因此它应该是相同的哈希值),而不是再次运行哈希值,而只是通过值的副本(因此它很便宜),但肯定是AnyProp的setter(因为它正在更改记录,因此会更改哈希值)将再次将散列设置为null,以便在下次请求时计算它(分解AnyProp的新值)和cop
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);
}