본문 바로가기
C#

[C#] 참조 형식(Reference Types)과 값 형식(Value Types)

by ifhead 2022. 9. 10.
반응형

C# 형식은 참조 형식과 값 형식 두 가지가 있습니다.

 

참조 형식 (Reference Types)

참조 형식은 Heap 공간에 데이터를 할당합니다. 힙 영역은 동적으로 할당되는 메모리로, 프로그래머가 원하는 시점에 메모리에 들어가는 변수들이 힙 영역으로 들어갑니다. 그리고 가비지 컬렉터는 주기적으로 힙 영역을 청소합니다. 힙 영역에 있는 값이 어떠한 스택에 의해서도 참조되고 있지 않으면 가비지컬렉션의 대상이 됩니다.

 

참조 형식의 변수에는 데이터(오브젝트)에 대한 '가리킴(참조)'이 저장됩니다. 우리가 어떤 파일을 폴더 안에 넣어두고, 바탕화면에는 바로가기 폴더를 만들어 놓듯이, 참조 변수는 실제 데이터가 어디에 있는지를 가리키는 정보를 가집니다.

 

값 형식의 변수에는 해당 데이터가 직접 들어가 있습니다. 참조 형식은 값이 아닌 '참조'를 저장하기 때문에 참조 형식이라 불립니다. 만약 object a = 100; 를 선언했다면, '100'이라는 값은 힙 영역에 저장되고, object a는 그 힙 영역을 가리키는 참조를 가지게 됩니다.

 

참조 형식은 두 가지 변수가 같은 개체를 참조할 수 있습니다. A라는 값을 참조하는 B와 C 변수가 있다고 해 봅시다. 그런데 B가 어떤 작업을 하면 값 A가 변동됩니다. 값 A가 변경됨에 따라 C도 같이 변동됩니다. 참조 변수 B가 참조 변수 C에 간접적으로 영향을 미칠 수 있는 것입니다.

 

반면 값 형식에서는 각 변수에 데이터 자체가 들어 있습니다. 그러므로 한 변수의 작업이 다른 변수에 영향을 미칠 수 없습니다.

 

참조 형식의 키워드

  • class
  • interface
  • delegate
  • record
  • dynamic
  • object
  • string

값 형식 (Value Types)

정적인 Stack 메모리에 할당됩니다. 스택 영역에 저장된 변수는 자신이 선언되어 있는 스코프가 끝나면 소멸됩니다. 값 형식은 메모리 공간에 데이터가 직접 생성됩니다. 이 생성된 데이터를 인스턴스라고 합니다. 스택은 가비지 컬렉터에 의해 정리되는 것이 아니라, 코드 블록이 시작되면서 메모리에 쌓이기 시작하고 스코프가 끝나면 역순으로 제거됩니다.

 

값 변수란 다음과 같은 상황에 '복사'됩니다.

 

  1. 할당될 때
  2. 인수를 메서드에 저장할 때
  3. 메서드 결과를 반환할 때

 

'복사된다'라는 것이 무슨 의미인지 간단한 예제를 통해 확인해보겠습니다.

 

using System;

public class Program {
  public int x = 0;

  public static void Main() {
    Program p = new Program();
    changeX(p.x);
    Console.WriteLine($"x is : {p.x}");
    p.x = 100;
    Console.WriteLine($"x is now : {p.x}");
  }

  public static void changeX(int _x) {
    _x = 100;
    Console.WriteLine($"x is : {_x}");
  }
}

//결과 
//x is : 100
//x is : 0
//x is now : 100

 

함수로 인해 p.x가 100으로 변경되어야 할 것 같은데, 두 번째 WriteLine의 결과는 그대로 0입니다.

이렇게 된 이유가 있습니다. p.x는 메소드로 전달될 때 값이 복사되었습니다. 함수는 복사된 값을 변경했습니다.

따라서 p.x의 실제 값은 변경되지 않았습니다.

 

값 형식의 키워드

  • byte
  • sbyte
  • short
  • ushort
  • int 
  • uint
  • long
  • ulong
  • float
  • double
  • decimal
  • char
  • bool
  • enum
  • struct

 

복사를 통해 값 형식인지 증명

 

값 형식은 할당될 때, 인수를 메서드에 저장할 때, 메서드 결과를 반환할 때 복사된다고 했습니다. 원래 데이터의 '값'만 복사되는 것이지 주소가 복사되는 것이 아닙니다. 따라서 값 형식의 변수는 메소드를 통해 조작될 수 없습니다. 아래 코드에서는 할당될 때 조작되지 않는지 검증하고 있습니다. 만약 할당을 통해 변수를 복사한 후 조작할 수 없다면 그 변수는 값 형식일 것입니다.

 

using System;

public class Program {
  public static void Main() {
    byte a = 1;
    sbyte b = 1;
    short c = 1;
    ushort d = 1;
    int e = 1;
    uint f = 1;
    long g = 1;
    ulong h = 1;
    float i = 1;
    double j = 1;
    decimal k = 1;
    char l = '1';
    bool m = true;
    O o = new O(1);

    Console.WriteLine(
        $"{a},{b},{c},{d},{e},{f},{g},{h},{i},{j},{k},{l},{m},{o.o1}"
    );

    byte aa = a;
    sbyte bb = b;
    short cc = c;
    ushort dd = d;
    int ee = e;
    uint ff = f;
    long gg = g;
    ulong hh = h;
    float ii = i;
    double jj = j;
    decimal kk = k;
    char ll = l;
    bool mm = m;
    O oo = o;

    aa = 0;
    bb = 0;
    cc = 0;
    dd = 0;
    ee = 0;
    ff = 0;
    gg = 0;
    hh = 0;
    ii = 0;
    jj = 0;
    kk = 0;
    ll = '0';
    mm = false;

    Console.WriteLine(
        $"{a},{b},{c},{d},{e},{f},{g},{h},{i},{j},{k},{l},{m},{o.o1}"
    );
  }

  struct O {
    public int o1;
    public O(int _o) { o1 = _o; }
  }
}

// 1,1,1,1,1,1,1,1,1,1,1,1,True,1
// 1,1,1,1,1,1,1,1,1,1,1,1,True,1

 


 

C# 데이터의 두 가지 분류

 

C#의 데이터 타입들

 

 

반응형

댓글