参照型のディープコピー(詳細コピー)
目次
配列(参照型)のコピー
メモリの同じところを指しているので、コピーしても同じところが変更されます。
サンプル
int[] array1 = { 1, 2, 3 };
int[] array2;
array2 = array1;
array2[1] = 10;
for (int i = 0; i < array1.Length; i++)
{
Console.WriteLine(array1[i]);
}
Console.WriteLine();
for (int i = 0; i < array2.Length; i++)
{
Console.WriteLine(array2[i]);
}
結果
1
10
3
1
10
3
影響を受けないコピー(その1)
サンプル
int[] array1 = { 1, 2, 3 };
int[] array2 = new int[array1.Length];
for (int i = 0; i < array1.Length; i++)
{
array2[i] = array1[i];
}
array2[1] = 10;
for (int i = 0; i < array1.Length; i++)
{
Console.WriteLine(array1[i]);
}
Console.WriteLine();
for (int i = 0; i < array2.Length; i++)
{
Console.WriteLine(array2[i]);
}
結果
1
2
3
1
10
3
影響を受けないコピー(その2)
サンプル
int[] array1 = { 1, 2, 3 };
int[] array2 = new int[array1.Length];
Array.Copy(array1, array2, array1.Length);
array2[1] = 10;
for (int i = 0; i < array1.Length; i++)
{
Console.WriteLine(array1[i]);
}
Console.WriteLine();
for (int i = 0; i < array2.Length; i++)
{
Console.WriteLine(array2[i]);
}
結果
1
10
3
1
10
3
Unity ゲームに適用してみると・・・
ObjectExtension.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class ObjectExtension
{
// ディープコピーの複製を作る拡張メソッド
public static T DeepClone<T>(this T src)
{
using (var memoryStream = new MemoryStream())
{
// オブジェクト全体を、バイナリ形式でシリアル化および逆シリアル化します。
var binaryFormatter = new BinaryFormatter();
// シリアライズ
binaryFormatter.Serialize(memoryStream, src);
memoryStream.Seek(0, SeekOrigin.Begin);
// デシリアライズ
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
Player.cs
using System;
[Serializable]
public class Player
{
public int Hp { get; set; }
public string Name { get; set; }
}
Sample.cs
using UnityEngine;
class Sample : MonoBehaviour
{
void Start()
{
Player player1 = new Player
{
Hp = 100,
Name = "Player1"
};
var player2 = player1.DeepClone();
player2.Name = "player2";
Debug.Log(player1.Hp + " " + player1.Name);
Debug.Log(player2.Hp + " " + player2.Name);
}
}
元の考え方
using System;
using System.Linq;
using UnityEngine;
// 配列に格納するクラス
[Serializable]
public class Player
{
public int No { get; set; }
public string Name { get; set; }
public override string ToString() => $"{No:00}:{Name}";
}
class Program : MonoBehaviour
{
void Start()
{
Player[] src =
{
new Player {No=1, Name="aaa" },
new Player {No=2, Name="bbb" },
new Player {No=3, Name="ccc" },
};
// ディープコピー
var clone = src.DeepClone();
Debug.Log($"複製先:{string.Join(", ", clone.Select(s => s.ToString()))}");
// 出力:
// 複製先:01:aaa, 02:bbb, 03:ccc
// 複製元のオブジェクトに変更を加える
src[1].Name = "ZZZ";
Debug.Log($"複製元:{string.Join(", ", src.Select(s => s.ToString()))}");
// 出力:
// 複製元:01:aaa, 02:ZZZ, 03:ccc
// しかし、複製先は変わっていない
Debug.Log($"複製先:{string.Join(", ", clone.Select(s => s.ToString()))}");
// 出力:
// 複製先:01:aaa, 02:bbb, 03:ccc
}
}
UnityEngineをシミュレートした場合
Object.cs
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace UnityEngine
{
// シリアル化したいクラスに必要なアトリビュート
[Serializable]
public class Object
{
// オブジェクトの名前
public string name;
// ディープコピー(深いコピー) インスタンス自体をコピーすること
public static T Instantiate<T>(T original) where T : Object
{
using (var memoryStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, original);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
}
ディスカッション
コメント一覧
まだ、コメントがありません