是否可以实现一种克隆对象并将更改应用于私有属性的方法(无需反射)?
Is it possible to implement a method that clones an object and applies changes to private properties (without reflection)?
DDD 中的值对象是不可变的,属性通常通过构造函数设置一次。
有时我需要一个值对象的副本,只有一些更改,例如应复制 10 个属性,其中一个 属性 将获得新值。在这种情况下,我想避免使用带有 11 个参数的构造函数,而是想实现一个 returns 副本但同时对属性应用一些更改的方法。我知道我可以通过反射来做到这一点,但想检查一下是否可以避免反射。
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;
}
}
之所以有效,是因为“栏”属性 有一个 public setter。我需要一些允许在私人成员时设置“酒吧”的东西。
var test = new Foo(1);
var clone = test.CloneAndApply(x => x.Bar = 2);
Console.WriteLine(clone.Bar);
您可能对 Records, introduced in C# 9. Records contain many parts, but one of them is support for "withers" 感兴趣,它与新的 init-only 属性交互,可以轻松创建记录的修改副本。
记录支持诸如主构造函数之类的东西,我将在这里略过。简单来说,您的记录可能如下所示:
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
实现。
长期计划是允许将 wither 用于非记录类型,尽管(从 C# 9 开始)这还不受支持。
如果你利用主构造器,你可以写得更简洁:
public record Foo(int Bar);
这会自动生成 Bar
属性,带有 get
和 init
访问器,以及一个分配给它的构造函数。
另一个解决方案可能是反射。这也可用于为私有属性赋值:
class Program
{
static void Main(string[] args)
{
var test = new Foo(1);
var clone = test.CloneAndApply(x => x.GetType().GetProperty("Bar").SetValue(x, 2));
Console.WriteLine(clone.Bar);
}
}
public class Foo
{
public int Bar { get; private set; }
public Foo(int bar)
{
Bar = bar;
}
public Foo CloneAndApply(Action<Foo> apply)
{
var result = new Foo(Bar);
apply(result);
return result;
}
}
我使用 WithX
方法实现了 .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);
}
}
DDD 中的值对象是不可变的,属性通常通过构造函数设置一次。
有时我需要一个值对象的副本,只有一些更改,例如应复制 10 个属性,其中一个 属性 将获得新值。在这种情况下,我想避免使用带有 11 个参数的构造函数,而是想实现一个 returns 副本但同时对属性应用一些更改的方法。我知道我可以通过反射来做到这一点,但想检查一下是否可以避免反射。
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;
}
}
之所以有效,是因为“栏”属性 有一个 public setter。我需要一些允许在私人成员时设置“酒吧”的东西。
var test = new Foo(1);
var clone = test.CloneAndApply(x => x.Bar = 2);
Console.WriteLine(clone.Bar);
您可能对 Records, introduced in C# 9. Records contain many parts, but one of them is support for "withers" 感兴趣,它与新的 init-only 属性交互,可以轻松创建记录的修改副本。
记录支持诸如主构造函数之类的东西,我将在这里略过。简单来说,您的记录可能如下所示:
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
实现。
长期计划是允许将 wither 用于非记录类型,尽管(从 C# 9 开始)这还不受支持。
如果你利用主构造器,你可以写得更简洁:
public record Foo(int Bar);
这会自动生成 Bar
属性,带有 get
和 init
访问器,以及一个分配给它的构造函数。
另一个解决方案可能是反射。这也可用于为私有属性赋值:
class Program
{
static void Main(string[] args)
{
var test = new Foo(1);
var clone = test.CloneAndApply(x => x.GetType().GetProperty("Bar").SetValue(x, 2));
Console.WriteLine(clone.Bar);
}
}
public class Foo
{
public int Bar { get; private set; }
public Foo(int bar)
{
Bar = bar;
}
public Foo CloneAndApply(Action<Foo> apply)
{
var result = new Foo(Bar);
apply(result);
return result;
}
}
我使用 WithX
方法实现了 .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);
}
}