C# 是否可以实现克隆对象并将更改应用于私有属性(无反射)的方法?

C# 是否可以实现克隆对象并将更改应用于私有属性(无反射)的方法?,c#,domain-driven-design,C#,Domain Driven Design,DDD中的值对象是不可变的,属性通常通过构造函数设置一次 我有时需要一个值对象的副本,但只需要一些更改,例如,应复制十个属性,而一个属性将获得新值。在本例中,我希望避免使用具有11个参数的构造函数,而是希望实现一个方法,该方法返回一个副本,但同时对属性应用一些更改。我知道我可以通过反射来做到这一点,但我想检查是否有可能避免反射 public class Foo { public int Bar { get; set; } // actual use case is { get; priv

DDD中的值对象是不可变的,属性通常通过构造函数设置一次

我有时需要一个值对象的副本,但只需要一些更改,例如,应复制十个属性,而一个属性将获得新值。在本例中,我希望避免使用具有11个参数的构造函数,而是希望实现一个方法,该方法返回一个副本,但同时对属性应用一些更改。我知道我可以通过反射来做到这一点,但我想检查是否有可能避免反射

public class Foo
{
    public int Bar { get; set; } // actual use case is { get; private set; }
    
    public Foo(int bar)
    {
        Bar = bar;
    }
    
    public Foo CloneAndApply(Action<Foo> apply)
    {
        var result = new Foo(Bar);
        apply(result);
        return result;
    }
}

您可能会对C#9中介绍的内容感兴趣。记录包含许多部分,但其中之一是对“”的支持,它与新的仅限init的属性交互,以便轻松创建记录的修改副本

记录支持诸如主构造函数之类的东西,我将在这里对此加以说明。简单来说,您的记录可能如下所示:

public record Foo
{
    public int Bar { get; init; }
    
    public Foo(int bar)
    {
        Bar = bar;
    }
}
可用于:

var foo = new Foo(3);
var foo2 = foo with { Bar = 4 };
此记录还根据其单个成员的相等性自动实现相等,并重写
ToString
实现

长期计划是允许withers与非记录类型一起使用,尽管(从C#9开始)这还不受支持


如果利用主构造函数,您可以更简洁地编写记录:

public record Foo(int Bar); 

这将自动生成带有
get
init
访问器的
Bar
属性,以及分配给它的构造函数。

另一种解决方案可能是反射。这也可用于为私有属性指定值:

类程序
{
静态void Main(字符串[]参数)
{
var测试=新的Foo(1);
var clone=test.CloneAndApply(x=>x.GetType().GetProperty(“Bar”).SetValue(x,2));
Console.WriteLine(clone.Bar);
}
}
公开课Foo
{
公共整型条{get;private set;}
公共Foo(int-bar)
{
巴=巴;
}
公共食品克隆应用(行动适用)
{
var结果=新的Foo(巴);
应用(结果);
返回结果;
}
}

我使用
with x
方法来实现.net5之前的记录,这些方法传递需要不同的属性。这不花哨,但很管用

public class Foo
{
    public int X { get; private set; }
    
    public int Bar { get; private set; }
    
    public Foo(int x, int bar) { X = x; Bar = bar; }
    
    public Foo WithBar(int bar) => new Foo(X, bar);
    }
}

很好,这是开始使用.NET5的一个原因——太糟糕了,我现在只能使用Core3.1。不过,我认为这就是答案,因为如果没有反射和C#<9,可能无法做到这一点。您可以在.NET Core 3.1上使用它,只要您使用.NET 5 SDK,并在csproj中设置
9
。您还需要
namespace System.Runtime.CompilerServices{internal static class IsExternalInit{}}
在项目中的某个地方。很高兴知道-我在一个团队中工作,这不是我可以做的临时更改,但看起来影响是可控的
public-Foo-CloneAndApply(int-bar)
不起作用吗?我知道你的例子很可能是你需要的一个简化版本,但是,也许你只需要几个不同的构造函数?@tymtam是的,这会起作用,但当我使用许多属性时,CloneAndApply的参数数量会非常多-而且每次添加或删除属性时,我都必须更改签名,而不是我要找的,因为它使用反射,这仍然是一个很好的例子,说明如何使用reflection@TvdH啊,我明白了。很抱歉只是出于兴趣,为什么不反思?有什么具体的原因吗?可能和5.0之前一样好这是Roslyn做的,FWIW
public class Foo
{
    public int X { get; private set; }
    
    public int Bar { get; private set; }
    
    public Foo(int x, int bar) { X = x; Bar = bar; }
    
    public Foo WithBar(int bar) => new Foo(X, bar);
    }
}