如何在 Tasks 中本地修改共享对象以防止副作用

How to Locally modify shared objects in Tasks to prevent Side Effects

提问人:SyndRain 提问时间:10/5/2023 最后编辑:SyndRain 更新时间:10/14/2023 访问量:95

问:

我有一个在并发任务之间共享的对象。它用于使用同一组属性创建不同的 JSON。这是其类的简化示例:(我称它为构建器,但它并没有真正遵循构建器模式。

class Builder {
    public string part1;
    public string part2;
    //More parts...

    public JSON BuildA(){
        return new Serialize(TypeA({"1": part1, "2": part2}));
    }
    public JSON BuildB(){
        return new Serialize(TypeB(part1.ToString(), part2 + part3));
    }
    //More Building methods
}

在将构建器传递给任务之前,将初始化部件。问题是在某些任务中,我可能希望在构建之前在本地覆盖一个或两个属性。由于构建器在任务之间共享,因此这些更改会对其他任务产生不必要的副作用。

Task.Run(() => MethodA(builder));
Task.Run(() => MethodB(builder));
Task.Run(() => MethodC(builder));

Response MethodA(Builder builder){
    builder.SetPart1(10);
    builder.SetPart16(false);
    JSON = builder.BuildC();
    //do http request stuffs
}

除了深度克隆我的构建器之外,还有另一种方法可以在防止副作用的同时对共享对象进行更改或覆盖?

注意:我使用的是 .NET 6


现在,我能想到的是将构建器对象作为本地属性传递,覆盖和修改调用 getter 的每个 getter 和位置:

JSON = builder.Build(new Builder(){part1=10, part16=false})

class Builder {
    public string getPart1(Builder overwrites){
        return overwrites.part1 ?? part1;
    }
}
c# 并发 任务 副作用 共享资源

评论


答:

0赞 Guru Stron 10/5/2023 #1

除了深度克隆我的构建器之外,还有另一种方法可以在防止副作用的同时对共享对象进行更改吗?

基本上没有。但是,您可以通过切换到记录来简化生活,这些记录可以更轻松地创建和管理不可变数据类型:

var builder = new Builder("one", "two");
var newBuilder = builder with { Part1 = "new" };

record Builder(string Part1, string Part2) {
    public JSON BuildA(){
        return new Serialize(TypeA({"1": part1, "2": part2}));
    }
    public JSON BuildB(){
        return new Serialize(TypeB(part1.ToString(), part2 + part3));
    }
}

笔记:

  1. 对于表达式,仍是浅拷贝属性,因此请对嵌套数据类型使用记录。

  2. 对于“可选”属性,可以使用 -only 属性:init

    var newBuilder = builder with { Part1 = "new", I = 5};
    
    record Builder(string Part1, string Part2) {
        public int I { get; init; }
        // ...
    }
    
  3. C# 记录 - 使用 with 关键字修改属性

评论

1赞 SyndRain 10/6/2023
我刚刚注意到浅层复制对象就足够了,因为我只改变非嵌套数据类型!在这种情况下,再完美不过了。谢谢!builder with { Part1 = "new", I = 5}
0赞 Guru Stron 10/6/2023
@SyndRain - 很乐意帮忙!