티스토리 뷰
개요
프로그래밍에서 Copy에는 Deep Copy(깊은 복사)와 Shallow Copy(얕은 복사) 두가지가 있다.
Shallow Copy
object class에 MemberwiseClone을 이용하여 Shallow Copy(얕은 복사)를 진행하였다.(Coffee의 Copy 함수는 는 더 아래 참고.)
class Program
{
static void Main(string[] args)
{
var coffee1 = new Coffee() { Shot = 2, CoffeeBean = new CoffeeBean { Origin = "Brazil"} };
var coffee2 = coffee1.Copy();
coffee2.Shot = 1;
coffee2.CoffeeBean.Origin = "Kenya";
Console.WriteLine($"Coffee1 - HashCode: {coffee1.GetHashCode()},\tShot: {coffee1.Shot},\tOrigin: {coffee1.CoffeeBean.Origin}");
Console.WriteLine($"Coffee2 - HashCode: {coffee2.GetHashCode()},\tShot: {coffee2.Shot},\tOrigin: {coffee2.CoffeeBean.Origin}");
// Output
// Coffee1 - HashCode: 58225482, Shot: 2, Origin: Kenya
// Coffee2 - HashCode: 54267293, Shot: 1, Origin: Kenya
}
}
출력 결과를 보면, Shot은 의도한대로 Coffee2의 값만 변화하였지만, Origin(원산지)는 의도하지 않게 Coffee1까지 변경되었다. Shallow Copy는 참조 형식의 객체는
Deep Copy
DeepCopy에는 여러가지 방법이 있다 일반적으로 new를 이용하여 새로운 객체를 생성하는 방법으로 많이 접근하지만 이는 새로운 field를 추가할 때 마다, 관련 카피 함수를 수정해야한다는 번거로움이 있다.
직열화/역질렬화를 이용한 DeepCopy 예제
따라서, 나는 Binary형태의 직렬화/역직렬화를 이용하여 DeepCopy를 구현해보았다.
public static class CoffeeExtensions
{
public static T DeepCopy<T>(this T source) where T : new()
{
if (!typeof(T).IsSerializable)
{
// fail
return source;
}
try
{
object result = null;
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, source);
ms.Position = 0;
result = (T)formatter.Deserialize(ms);
ms.Close();
}
return (T)result;
}
catch (Exception)
{
// fail
return new T();
}
}
}
해당 확장 메서드를 이용하여 Deep Copy의 출력결과는 아래와 같다.
class Program
{
static void Main(string[] args)
{
var coffee1 = new Coffee() { Shot = 2, CoffeeBean = new CoffeeBean { Origin = "Brazil"} };
var coffee2 = coffee1.Copy();
coffee2.Shot = 1;
coffee2.CoffeeBean.Origin = "Kenya";
Console.WriteLine($"Coffee1 - HashCode: {coffee1.GetHashCode()},\tShot: {coffee1.Shot},\tOrigin: {coffee1.CoffeeBean.Origin}");
Console.WriteLine($"Coffee2 - HashCode: {coffee2.GetHashCode()},\tShot: {coffee2.Shot},\tOrigin: {coffee2.CoffeeBean.Origin}");
var coffee3 = coffee1.DeepCopy();
coffee3.Shot = 3;
coffee3.CoffeeBean.Origin = "Ethiopia";
Console.WriteLine($"Coffee1 - HashCode: {coffee1.GetHashCode()},\tShot: {coffee1.Shot},\tOrigin: {coffee1.CoffeeBean.Origin}");
Console.WriteLine($"Coffee3 - HashCode: {coffee3.GetHashCode()},\tShot: {coffee3.Shot},\tOrigin: {coffee3.CoffeeBean.Origin}");
Console.ReadLine();
// Output
//Coffee1 - HashCode: 58225482, Shot: 2, Origin: Kenya
//Coffee3 - HashCode: 55530882, Shot: 3, Origin: Ethiopia
}
}
DeepCopy를 이용하니 나의 의도대로 Coffee3의 원산지만 잘 변경되었다.
* 주의 할것은 직렬화를 사용하기 위해서는 해당 객체에 [Serializable] Attribute가 선언되어야 한다.
[Serializable]
public class Coffee
{
public int Shot { get; set; }
public CoffeeBean CoffeeBean { get; set; }
public Coffee Copy()
{
return (Coffee)this.MemberwiseClone();
}
}
[Serializable]
public class CoffeeBean
{
public string Origin { get; set; }
}
Reference
https://docs.microsoft.com/ko-kr/dotnet/api/system.object.memberwiseclone?view=net-6.0
https://aspdotnet.tistory.com/2122
'Computer Engineering > C#(.Net)' 카테고리의 다른 글
[C#] Await, Async - 비동기 프로그래밍 (0) | 2022.06.28 |
---|---|
[C#] 7.0 문법 - 튜플 필드의 이름 주기 (0) | 2022.06.22 |
[C#] Attribute([])를 알아보자.! (0) | 2022.05.12 |
[C#] string 과 StringBuilder (0) | 2022.04.13 |
[C#] VScode에서 C# 콘솔 어플리케이션 만들기 (0) | 2022.04.05 |