반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- AfterCheck
- MSSQL
- footer
- SQL Server
- header
- jQuery
- checkbox
- github
- input
- C#
- Compare
- 한번에 체크
- json
- 깜빡임
- SSMS
- 윈폼
- treeview
- html 코드
- 하위노드
- WinForm
- Git
- 깃허브
- body
- 비교
- CheckAllChildNodes
- nodejs
- 로깅
- 초기설정
- ejs
- sql 서버
Archives
- Today
- Total
타닥타닥 민타쿠
C# 객체 복사 (Deep Copy) 클래스 본문
반응형
결론부터 말하자면, 아래 클래스 저장 후 ObjectExtensions.Copy(객체) 의 형태로 쓰자.
깊은 복사가 된 객체를 반환 받을 수 있다.
다만 조금 느릴 수 있다.
public static class ObjectExtensions // Deep Copy Class. Use 'ObjectExtensions.Copy(object)'.
{
private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsPrimitive(this Type type)
{
if (type == typeof(String)) return true;
return (type.IsValueType & type.IsPrimitive);
}
public static Object Copy(this Object originalObject)
{
return InternalCopy(originalObject, new Dictionary<Object, Object>(new ReferenceEqualityComparer()));
}
private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
{
if (originalObject == null) return null;
var typeToReflect = originalObject.GetType();
if (IsPrimitive(typeToReflect)) return originalObject;
if (visited.ContainsKey(originalObject)) return visited[originalObject];
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
var cloneObject = CloneMethod.Invoke(originalObject, null);
if (typeToReflect.IsArray)
{
var arrayType = typeToReflect.GetElementType();
if (IsPrimitive(arrayType) == false)
{
Array clonedArray = (Array)cloneObject;
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
}
}
visited.Add(originalObject, cloneObject);
CopyFields(originalObject, visited, cloneObject, typeToReflect);
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
return cloneObject;
}
private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
{
if (typeToReflect.BaseType != null)
{
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
}
}
private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
{
foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
{
if (filter != null && filter(fieldInfo) == false) continue;
if (IsPrimitive(fieldInfo.FieldType)) continue;
var originalFieldValue = fieldInfo.GetValue(originalObject);
var clonedFieldValue = InternalCopy(originalFieldValue, visited);
fieldInfo.SetValue(cloneObject, clonedFieldValue);
}
}
public static T Copy<T>(this T original)
{
return (T)Copy((Object)original);
}
}
public class ReferenceEqualityComparer : EqualityComparer<Object>
{
public override bool Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
public override int GetHashCode(object obj)
{
if (obj == null) return 0;
return obj.GetHashCode();
}
}
namespace ArrayExtensions
{
public static class ArrayExtensions
{
public static void ForEach(this Array array, Action<Array, int[]> action)
{
if (array.LongLength == 0) return;
ArrayTraverse walker = new ArrayTraverse(array);
do action(array, walker.Position);
while (walker.Step());
}
}
internal class ArrayTraverse
{
public int[] Position;
private int[] maxLengths;
public ArrayTraverse(Array array)
{
maxLengths = new int[array.Rank];
for (int i = 0; i < array.Rank; ++i)
{
maxLengths[i] = array.GetLength(i) - 1;
}
Position = new int[array.Rank];
}
public bool Step()
{
for (int i = 0; i < Position.Length; ++i)
{
if (Position[i] < maxLengths[i])
{
Position[i]++;
for (int j = 0; j < i; j++)
{
Position[j] = 0;
}
return true;
}
}
return false;
}
}
}
얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)에 대한 짧은 설명
코드를 짜다보면 참조를 공유하지 않고 완전히 똑같지만 새로운 객체가 필요한 경우가 생긴다. 내가 그랬다.
보통
Object something = new Something();
something = originObject;
위와 같은 방식으로 새로운 객체를 만들어 냈다고 생각하는 경우가 있는데,
이는 새로운 객체가 기존의 객체를 바라보는 형태이므로 originObject 가 수정되면 something 도 같이 변한다.
(정확히는 변하는 것이 아닌 같은 주소를 참조하기 때문에 같은 것이라 할 수 있다)
이를 얕은 복사(Shallow Copy)라 부른다.
물론 얕은 복사가 필요한 경우가 있겠지만, 지금은 값은 같지만 새로운 주소를 가지게 하는 깊은 복사(Deep Copy)가 필요했다.
일일이 객체 속을 낱낱히 뒤져가며 해야 하나 싶었지만 다행히 그리고 고맙게도 Stack Overflow 에서 해결책을 찾았다.
https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-of-an-object-in-net
반응형
'개발 > Winform' 카테고리의 다른 글
C# 윈폼(Winform) 자식(하위) 노드까지 한번에 체크하기 (0) | 2022.07.23 |
---|---|
C# Winform(윈폼) 트리뷰 체크박스 더블클릭 버그 방지 해결책 (0) | 2022.07.22 |
윈폼 테이블 변경이 느릴 때(C# Winform TableLayoutPanel Suspend) (0) | 2021.09.10 |
Comments