C#で自作したクラスのListを複製(値コピー/DeepCopy)する方法でネット検索すると、ToList()したり、newしたりする方法が出て来ますが、DeepCopy()メソッドを作るのが無難です。
MemberwiseClone()を使った DeepCopy の例 だと、データクラスに処理メソッドを持たせることでソースコードが煩雑になる問題がありますが、拡張メソッドにしたDeepCopy()メソッドだとデータクラスに処理を持たせる必要が無く、ソースコードがシンプルになります。
ソースコードはGitHubで公開しています。
1 2 3 4 5 6 7 |
var testDataAList = new List<TestDataA>(); testDataAList.Add(new TestDataA() { AAA = "あ" }); testDataAList.Add(new TestDataA() { AAA = "い" }); var testDataAList2 = testDataAList.DeepCopy(); |
BinaryFormatter()を使った場合に出力されていた警告
以前投稿した C#でListを値コピー(DeepCopy)する際のお勧め は警告が出力される為、今回、「チェックが厳しい現場だと使えない」という問題に対処しました。
1 2 3 4 |
警告 SYSLIB0011 'BinaryFormatter.Deserialize(Stream)' は旧形式です ('BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.') ConsoleApp1 C:\Work\5_UnikktleBlog\BlazorServer5.0\Src_DeepCopy - コピー\ConsoleApp1\ConsoleApp1\Utils\CopyHelper.cs 21 警告 SYSLIB0011 'BinaryFormatter.Serialize(Stream, object)' は旧形式です ('BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.') ConsoleApp1 C:\Work\5_UnikktleBlog\BlazorServer5.0\Src_DeepCopy - コピー\ConsoleApp1\ConsoleApp1\Utils\CopyHelper.cs 18 アクティブ |
ソースコード変更内容を解説
Utils/CopyHelper.cs
・DeepCopy共通部品。
・ジェネリックなのでどのデータ型にも対応できます。
・JsonSerializerOptionsにReferenceHandler.Preserveを指定することで、複雑で大きいデータクラスのコピーもできるようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using System.Text.Json; using System.Text.Json.Serialization; namespace ConsoleApp1.Utils { public static class CopyHelper { /// <summary> /// オブジェクトの値コピーを容易にする拡張メソッド。 /// </summary> public static T DeepCopy<T>(this T src) { var jsonSerializerOptions = new JsonSerializerOptions() { ReferenceHandler = ReferenceHandler.Preserve, WriteIndented = true }; var jsonData = JsonSerializer.Serialize(src, jsonSerializerOptions); return JsonSerializer.Deserialize<T>(jsonData, jsonSerializerOptions); } } } |
Utils/CopyHelperTests.cs
・DeepCopy() を使った値コピーのテストプログラム。
・DeepCopy()元のインスタンスの値を変更しても、DeepCopy()先のインスタンスには影響が無いことを確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using ConsoleApp1.Utils; namespace ConsoleApp1.Utils.Tests { [TestClass()] public class CopyHelperTests { public class TestDataA { public string AAA { get; set; } } public class TestDataB { public List<TestDataA> TestDataAList { get; set; } = new(); } [TestMethod()] public void DeepCopyTest1() { var testData1 = new TestDataA() { AAA = "aaa" }; var testData2 = testData1.DeepCopy(); testData1.AAA = "bbb"; Assert.AreEqual(testData2.AAA, "aaa"); } [TestMethod()] public void DeepCopyTest2() { var testData1 = new TestDataA() { AAA = "あ値" }; var testData2 = testData1.DeepCopy(); testData1.AAA = "bbb"; Assert.AreEqual(testData2.AAA, "あ値"); } [TestMethod()] public void DeepCopyTest3() { var testDataB = new TestDataB(); testDataB.TestDataAList.Add(new TestDataA() { AAA = "あ" }); testDataB.TestDataAList.Add(new TestDataA() { AAA = "い" }); var testDataB2 = testDataB.DeepCopy(); testDataB.TestDataAList[0].AAA = "b"; Assert.AreEqual(testDataB2.TestDataAList[0].AAA, "あ"); } [TestMethod()] public void DeepCopyTest4() { var testDataAList = new List<TestDataA>(); testDataAList.Add(new TestDataA() { AAA = "あ" }); testDataAList.Add(new TestDataA() { AAA = "い" }); var testDataAList2 = testDataAList.DeepCopy(); testDataAList[0].AAA = "b"; Assert.AreEqual(testDataAList2[0].AAA, "あ"); } } } |
ソースコード構成
コンソールアプリ テンプレートプロジェクトに、ConsoleApp1Tests プロジェクトを追加しています。
コメント