- 2023/5/31 内容を修正
何回かに分けて DeepCopy.Expression の紹介を書いていきます。
今回は、DeepCopy.Expression が読み取り専用のメンバを持つクラスのディープコピーに対応していることついて紹介します。
C#では、読み取り専用のプロパティやフィールドを持つクラスをシリアライズ/デシリアライズを利用してディープコピーする場合は、対象のクラス側がそれに対応する実装になっている必要があります。
このことを確認するために、以下のような _name という読み取り専用フィールドと Child と UniqueId という読み取り専用プロパティを持つクラスを用意しました。
_name と Child はコンストラクタの引数によって値が設定されますが、UniqueId はコンストラクタ内部で自動割り当てされるようにしています。
public sealed class Item
{
private readonly string _name;
public Item(string name, Item? child = null)
{
_name = name;
Child = child;
UniqueId = Guid.NewGuid().ToString();
}
public string Name => _name;
public Item? Child { get; }
public string UniqueId { get; }
}
シリアライザでディープコピー
このクラスを JsonSerializer を用いてディープコピーした場合は次のような結果となります。
public sealed class Item
var item = new Item("foo", new Item("bar"));
var json = JsonSerializer.Serialize(item);
var cloned = JsonSerializer.Deserialize- (json);
Console.WriteLine($"[original] Name = {item.Name}, Child = {item.Child?.Name}, UniqueId = {item.UniqueId}");
Console.WriteLine($"[cloned] Name = {cloned?.Name}, Child = {cloned?.Child?.Name}, UniqueId = {cloned?.UniqueId}");
[original] Name = foo, Child = bar, UniqueId = 5e167a0c-2f56-457e-94f7-553492500c76 [cloned] Name = foo, Child = bar, UniqueId = 48fea065-6c9b-4c54-8150-9d621661f496
コンストラクタの引数で初期化している _name と Child はコピーされていますが、UniqueId は別の値になってしまいました。コンストラクタで初期化されないメンバも含めてディープコピーするには、対象のメンバに init アクセサを追加することで可能になります。
public sealed class Item
{
private readonly string _name;
public Item(string name, Item? child = null)
{
_name = name;
Child = child;
UniqueId = Guid.NewGuid().ToString();
}
public string Name => _name;
public Item? Child { get; }
public string UniqueId { get; init; }
}
これで再度シリアライザでコピーすると期待する結果が得られます。
[original] Name = foo, Child = bar, UniqueId = ea0b669a-c77e-4d0b-a899-5d1949b3201a [cloned] Name = foo, Child = bar, UniqueId = ea0b669a-c77e-4d0b-a899-5d1949b3201a
では、次に DeepCopy.Expression で元のクラスのインスタンスをコピーしてみます。
DeepCopy.Expression でコピー
DeepCopy.Expression は、読み取り専用のメンバも含めてディープコピーすることが出来ます。
var item = new Item("foo", new Item("bar"));
var cloned = ObjectCloner.Clone(item);
Console.WriteLine($"[original] Name = {item.Name}, Child = {item.Child?.Name}, UniqueId = {item.UniqueId}");
Console.WriteLine($"[cloned] Name = {cloned?.Name}, Child = {cloned?.Child?.Name}, UniqueId = {cloned?.UniqueId}");
[original] Name = foo, Child = bar, UniqueId = 2f8a9900-0485-4131-9a9d-b8ee2c388dfd [cloned] Name = foo, Child = bar, UniqueId = 2f8a9900-0485-4131-9a9d-b8ee2c388dfd
まとめ
DeepCopy.Expression を使えば、読み取り専用メンバを持つクラスのインスタンスも、元のクラスを変更することなく、そのままディープコピーする事が可能です。