C\#깊이 복사 에 대한 심도 있 는 해석

머리말
앞에서 우 리 는 디자인 모델 을 배 웠 는데 그 중에서 우 리 는 원형 모델 을 알 게 되 었 다.여기 에는 복제 자체 의 대상 이 언급 되 어 있다.그럼 대상 을 복사 하 는 거 야?여기에 이런 개념 이 언급 되 었 다.깊 고 얕 은 복사,깊 은 복사 가 무엇 입 니까?얕 은 복사 가 무엇 입 니까?같이 보 자.
얕 은 복사
우선 얕 은 복사 본 을 봅 시다.얕 은 복사 본 은 대상 의 모든 필드 를 새로운 대상 에 복사 하 는 것 으로 얕 은 복사 본 은 값 유형 과 인용 유형 에 서로 다른 영향 을 미친다.값 형식의 값 이 던 전 으로 복사 되면 던 전의 값 을 수정 하면 원래 대상 의 값 에 영향 을 주지 않 습 니 다.그러나 인용 형식 이 복사 본 에 복 사 된 것 은 인용 형식의 인용 입 니 다.인용 대상 이 아니다.이렇게 복사 본 중의 값 을 수정 하면 원래 대상 의 값 도 수정 된다.그러나 이 인용 형식 은 문자열 String 형식 을 제외 해 야 합 니 다.
그렇다면 왜 인용 형식 수정 사본 의 값 은 원래 대상 의 값 변 화 를 일 으 키 고 string 문자열 형식 은 제 외 됩 니까?우선 이러한 개념 을 알 아야 합 니 다.string 형식 은 가 변 적 이지 않 은 데이터 형식 입 니 다.즉,문자열 대상 을 초기 화 했다 는 것 을 의미 합 니 다.이 문자열 대상 은 바 꿀 수 없습니다.겉으로 문자열 의 내용 을 수정 하 는 방법 과 연산 은 실제로 새로운 문자열 을 만 든 다음 필요 에 따라 오래된 문자열 의 내용 을 새 문자열 로 복사 할 수 있 습 니 다.어떻게 이해 해?우 리 는 아래 의 이 사례 를 본다.

  #region      
  /// <summary>
  ///              
  /// </summary>
  /// <param name="o"></param>
  /// <returns></returns>
  public static string getMemory(object o)
  {
   GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
   IntPtr addr = h.AddrOfPinnedObject();
   return "0x" + addr.ToString("X");
  }
  /// <summary>
  ///      
  /// </summary>
  public static void Compares()
  {
   string a = "123";
   Console.WriteLine("a     :\t\t" + getMemory(a));
   string b = "123";
   Console.WriteLine("b     :\t\t" + getMemory(b));
   Console.WriteLine("a b   :\t\t" + Object.ReferenceEquals(a, b));
   b = "456";
   Console.WriteLine("b     :\t\t" + getMemory(b));


  }

  #endregion

여기 서 우 리 는 a="123",b="123"을 본다.우 리 는 그들의 인용 주 소 를 보 았 다.즉,우리 가 먼저 a 를 만 들 때 문자열 a 를 만 들 었 고 인용 주소 가 생 겼 다 는 것 이다.그리고 b 를 만 들 때 같은 값 이 있 는 지 먼저 찾 습 니 다.같은 값 이 존재 하면 인용 주 소 를 가 져 옵 니 다.이것 이 바로 a 와 b 의 인용 주소 가 같은 이유 이다.여기 에는 문자 주둔지 라 는 것 이 관련 되 어 있다.문자열 을 저장 합 니 다.그러면 다음 에 우 리 는 b 의 값 을 수정 하고 인용 주 소 를 출력 합 니 다.이전의 인용 주소 와 다르다 는 것 을 알 게 되 었 습 니 다.설명 은 원래 값 을 수정 하 는 것 이 아니 라 문자열 을 다시 만 들 고 인용 주 소 를 다시 가 져 왔 습 니 다.
다음은 얕 은 복사 사례 를 살 펴 보 겠 습 니 다.먼저 다음 과 같은 데이터 형식의 값 을 준비 합 니 다.int,string,enum,struct,class,int[],string[].

 /// <summary>
 ///   
 /// </summary>
 public enum EnumTest
 {
  TestOne = 1,
  TestTwo = 2
 }

 /// <summary>
 ///    
 /// </summary>
 public struct StructTest
 {
  public int Test;
  public StructTest(int i)
  {
   Test = i;
  }
 }

 /// <summary>
 ///  
 /// </summary>
 public class ClassTest
 {
  public string TestString;
  public ClassTest(string _string)
  {
   TestString = _string;
  }
 }
 /// <summary>
 ///    
 /// </summary>
 public class DeepClone : ICloneable
 {
  public int _int = 1;
  public string _string = "1";
  public EnumTest _enum = EnumTest.TestOne;
  public StructTest _struct = new StructTest(1);
  public ClassTest _class = new ClassTest("1");
  public int[] arrInt = new int[] { 1 };
  public string[] arrString = new string[] { "1" };
  public object Clone()
  {
   var NewOne = JsonConvert.SerializeObject(this);
   return JsonConvert.DeserializeObject<DeepClone>(NewOne);
  } 
 }
 class Program
 {
  static void Main(string[] args)
  {
   DeepClone simple = new DeepClone();
   var simpleTwo = (DeepClone)simple.Clone();
   simpleTwo._int = 2;
   simpleTwo._string = "2";
   simpleTwo._enum = EnumTest.TestTwo;
   simpleTwo._struct.Test = 2;
   simpleTwo._class.TestString = "2";
   simpleTwo.arrInt[0] = 2;
   simpleTwo.arrString[0] = "2";

   Console.WriteLine($"int          :{simple._int}\t\t        :{simpleTwo._int}");
   Console.WriteLine($"string         :{simple._string}\t\t       :{simpleTwo._string}");
   Console.WriteLine($"enum         :{(int)simple._enum}\t\t       :{(int)simpleTwo._enum}");
   Console.WriteLine($"struct         :{simple._struct.Test}\t\t      :{simpleTwo._struct.Test}");
   Console.WriteLine($"class         :{simple._class.TestString}\t\t     :{simpleTwo._class.TestString}");
   Console.WriteLine($"int           :{simple.arrInt[0]}\t\t       :{simpleTwo.arrInt[0]}");
   Console.WriteLine($"string           :{simple.arrString[0]}\t\t     :{simpleTwo.arrString[0]}");
  } 
 }

우 리 는 ICloneable 인 터 페 이 스 를 계승 하여 이 유형 들 을 모두 얕 게 복사 한 후에 복사 본 대상 을 수정 했다.출력 대상 과 던 전 대상 을 비교 합 니 다.int,enum,struct,값 유형 및 string 이라는 특수 한 인용 유형의 원래 대상 값 이 변 하지 않 았 음 을 발 견 했 습 니 다.그러나 class,int[],string[]이 인용 유형 대상 의 원래 대상 이 영향 을 받 아 값 이 바 뀌 었 습 니 다.우리 가 앞에서 말 한 것 을 다시 한 번 검증 했다.얕 은 복사 본 은 대상 을 복사 본 대상 에 할당 하고 값 형식 복사 값 을 참조 형식 으로 참조 대상 을 복사 하 는 것 입 니 다.복사 본 대상 값 을 수정 합 니 다.값 형식 과 string 원래 대상 은 영향 을 받 지 않 습 니 다.인용 유형 은 string 을 제외 하고 원래 대상 은 모두 영향 을 받 습 니 다.
딥 카피
우리 위 에서 얕 은 복사 본 을 보 았 는데,얕 은 복사 본 은 여전히 일정한 영향 을 미 치 므 로,잘 처리 하지 못 하면 bug 가 될 수 있다.그럼 그 에 대응 하 는 딥 카피 가 어떤 건 지 볼 까요?여기 서 먼저 설명 할 수 있 습 니 다.깊이 복사 하 는 것 은 값 유형 과 인용 유형 에 대해 다 르 지 않 습 니 다.딥 복사 도 대상 의 모든 필드 를 새 대상 에 복사 하지만 대상 은 값 형식 이 든 인용 형식 이 든 다시 만 들 고 복사 본 대상 으로 복사 합 니 다.던 전 대상 에 대한 수정 은 어떠한 유형 에 도 영향 을 주지 않 습 니 다.
우 리 는 위의 예 를 계속 깊이 복사 해 보 자.

 /// <summary>
 ///    
 /// </summary>
 public class DeepClone : ICloneable
 {
  public int _int = 1;
  public string _string = "1";
  public EnumTest _enum = EnumTest.TestOne;
  public StructTest _struct = new StructTest(1);
  public ClassTest _class = new ClassTest("1");
  public int[] arrInt = new int[] { 1 };
  public string[] arrString = new string[] { "1" };
  public object Clone()
  {
   var NewOne = JsonConvert.SerializeObject(this);
   return JsonConvert.DeserializeObject<DeepClone>(NewOne);
  } 
 }

 class Program
 {
  static void Main(string[] args)
  {
   DeepClone simple = new DeepClone();
   var simpleTwo = (DeepClone)simple.Clone();
   simpleTwo._int = 2;
   simpleTwo._string = "2";
   simpleTwo._enum = EnumTest.TestTwo;
   simpleTwo._struct.Test = 2;
   simpleTwo._class.TestString = "2";
   simpleTwo.arrInt[0] = 2;
   simpleTwo.arrString[0] = "2";

   Console.WriteLine($"int          :{simple._int}\t\t        :{simpleTwo._int}");
   Console.WriteLine($"string         :{simple._string}\t\t       :{simpleTwo._string}");
   Console.WriteLine($"enum         :{(int)simple._enum}\t\t       :{(int)simpleTwo._enum}");
   Console.WriteLine($"struct         :{simple._struct.Test}\t\t      :{simpleTwo._struct.Test}");
   Console.WriteLine($"class         :{simple._class.TestString}\t\t     :{simpleTwo._class.TestString}");
   Console.WriteLine($"int           :{simple.arrInt[0]}\t\t       :{simpleTwo.arrInt[0]}");
   Console.WriteLine($"string           :{simple.arrString[0]}\t\t     :{simpleTwo.arrString[0]}");
  } 
 }

여기 서 우 리 는 이 실행 결 과 를 보 았 습 니 다.값 유형 이 든 인용 유형 이 든 복사 본 대상 을 수정 한 후에 원래 대상 의 값 에 영향 을 주지 않 았 습 니 다.이것 이 바로 딥 카피 의 특징 이다.
총결산
우 리 는 얕 은 복사 와 깊 은 복사 본 을 보고 자세히 돌 아 봤 다.얕 은 복사 본 은 대상 의 필드 를 새로운 대상 에 복사 합 니 다.그러나 새로운 대상 을 수정 할 때 값 형식 과 string 형식의 필드 는 원래 대상 의 필드 에 영향 을 주지 않 습 니 다.인용 유형 은 string 형식 을 제외 하고 원래 대상 의 값 에 영향 을 줍 니 다.깊 은 복사 도 대상 의 필드 를 새로운 대상 에 복사 하지만 값 유형 이 든 인용 유형의 변화 든 원래 대상 의 값 에 영향 을 주지 않 습 니 다.깊 은 복사 본 은 원래 의 대상 을 다시 만 들 고 복사 본 대상 에 복사 하기 때문이다.
자,이상 이 이 글 의 모든 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가 치 를 가지 기 를 바 랍 니 다.여러분 의 저희 에 대한 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기