(주말공부)XR 플밍 - (8) Record (4/6)

이형원·2025년 4월 6일
0

XR플밍

목록 보기
35/215

0. 들어가기에 앞서 (삽질의 시작)

C#이나 Visual Studio도 기능 추가를 위해서 업데이트가 진행될 것이다. 작성 시점 C#의 버전은 14까지나 있는 것으로 확인이 되고, 버전이 업그레이드 되면서 많은 기능이 추가되거나 삭제되었을 것이다.

여기에서, C# 9.0에서 추가된 record라는 것을 알아보고자 한다.

...라고 써 볼랬는데 자동완성은 되면서도 안 써지는 모습이다.
그러면 버전 업그레이드를 해야 하나 보다. 버전 업그레이드 할 방법을 구글링 해봤다.


[.net 9.0 SDK 다운로드]
https://dotnet.microsoft.com/ko-kr/download/dotnet/9.0

마이크로소프트의 공식 사이트에 다운 받을 수 있다.


위와 같이 다운 받고 Visual Studio를 들어가보자.

(이때부터 뭔가 쎄함을 느꼈다)
업데이트가 가능하다고는 뜨는데, .net 업그레이드가 안 뜬다. 일단은 업데이트를 해 보았지만 C# 업그레이드는 되지 않았고 왜 안 되는지 여러모로 뒤적여봤지만... 결국 해결 방법을 찾지 못했다.

그 와중에 버전 업그레이드를 하면은 기존 다른 버전으로 작업하던 파일에 손상이 갈 수도 있다고 해서... 이번에는 직접 사용해보지는 않고 공부하는 용도로만 배운 것들을 정리해 놓고자 한다.

1. record란?

Record는 사용자정의 type을 생성할 수 있는 또 다른 방법이다.

클래스 또는 구조체 달리 선언할 수 있는 record 한정자는 주 역할이 데이터를 저장하는 형식에 유용한 멤버를 합성하도록 컴파일러에 지시한다.

  • 사용 목적
    record 타입은 객체 내의 멤버가 변하지 않는 Immutable Reference Type(불변 객체)을 만들기 위한 것이다. C# 9에서는 이를 위해 record 라는 새로운 키워드를 도입했으며, class 키워드를 사용해서 클래스를 정의하는 것이 아니라, 이 record라는 키워드를 통해 Immutable Type을 정의하게 된다.

record 내에 있는 모든 멤버 변수는 불변의 값을 갖게 되며 클래스처럼 상속 등의 기능을 사용할 수 있다.

선언 방법

// 기본형
// (접근자) record (이름)

ex)
public record Monster
{
	public string name;
    public int level;
    public int attack;
    public int hp;
}

이와 같이 클래스를 선언하는 것과 비슷한 양상이지만, 클래스와는 다른 특성을 가지고 있다.
차이가 발생하는 것은 객체가 같은 지를 비교하는 과정에서 발생한다.

  1. 클래스에서의 객체의 동일 비교
  • 동일한 클래스
  • 두 변수가 같을 경우, 동일로 간주
  1. record에서의 객체의 동일 비교
  • 동일한 record 타입
  • 비교하는 record의 값이 같을 경우, 동일로 간주

이 둘을 비교하기 위한 예시를 아래에 두고자 한다.
아래와 같이 Class와 record의 코드를 짜 보았다고 해 보자.

public class Monster
{
    public int id;
    public string name;
    public int hp;
    public int atk;
    public int def;

    public Monster(int id, string name, int hp, int atk, int def)
    {
        this.id = id; 
        this.name = name;
        this.hp = hp;
        this.atk = atk;
        this.def = def;
    }
}
internal class Program
{
    static void Main(string[] args)
    {
        Monster monster1 = new Monster(1001, "달팽이", 100, 10, 5);
        Monster monster2 = new Monster(1001, "달팽이", 100, 10, 5);

        Console.WriteLine($"두 몬스터는 같은가? : {monster1.Equals(monster2)}");
        Console.WriteLine($"두 몬스터의 주소는 같은가? : {ReferenceEquals(monster1, monster2)}");
    }
}

Class 에서는 두 몬스터는 같지 않고, 두 몬스터의 주소 또한 같지 않다고 나온다.
반면에 record도 같은 방식으로 작성하면 결과가 다르게 나온다.

public record  Monster
{
    public int id;
    public string name;
    public int hp;
    public int atk;
    public int def;

    public Monster(int id, string name, int hp, int atk, int def)
    {
        this.id = id;
        this.name = name;
        this.hp = hp;
        this.atk = atk;
        this.def = def;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Monster monster1 = new Monster(1001, "달팽이", 100, 10, 5);
        Monster monster2 = new Monster(1001, "달팽이", 100, 10, 5);

        Console.WriteLine($"두 몬스터는 같은가? : {monster1.Equals(monster2)}");
        Console.WriteLine($"두 몬스터의 주소는 같은가? : {ReferenceEquals(monster1, monster2)}");
    }
}

이에 대한 결과는 두 몬스터는 같고, 두 몬스터의 주소는 다르다고 나온다.

monster1 과 monster2 자체는 별개로 선언했지만, 두 몬스터가 가진 값 자체는 같기 때문에 같은 것으로 간주하는 것이다.

2. record는 어떻게 사용할까?

2.1 with 표현식

Class의 경우에 객체를 생성할 때 그 변수를 일일히 입력해야 할 것이다. 하지만 record로 만든 객체를 활용하면 비슷한 객체를 좀 더 간단하게 표현할 수 있다.

//Monser record(상기 참고)

class Program
{
    static void Main(string[] args)
    {
    	// id: 1001, 이름: 달팽이, 체력: 100, 공격력: 10, 방어력: 5
        Monster monster1 = new Monster(1001, "달팽이", 100, 10, 5);
        
        // id: 1002, 이름: 버섯, 체력: 100, 공격력: 10, 방어력: 5
        Monster monster2 = new Monster(id = 1002, name = "버섯");
    }
}

2.2 init only setter

C# 9.0 에서 사용할 수 있는 기능으로 init setter가 추가되었다. 이는 setter를 선언할 때 최초에 초기화한 값에서 변경되지 않도록, 불변의 속성을 부여하는 것이다.

public record Monster
{
	public string name { get; init; }
    public int level { get; init; }
}

class Program
{
    static void Main(string[] args)
    {
        Monster monster = new Monster
        {
            name = "달팽이",
            level = 5
        };
    }
}

2.3 Positional record

record는 생성자(Constructor)과 Deconstructor을 사용할 수 있다. 이때, 생성자와 Deconstruct를 정의할 필요 없이 아래와 같이 사용하면 멤버 변수를 간단하게 가져올 수 있다.

public record Monster(string name, int level);

// 예제(7) 
class Program
{
    static void Main(string[] args)
    {
        Monster dragon = new Monster("드래곤", 50);

        // Deconstructor 사용
        var (name, level) = dragon;

        Console.WriteLine($"{name}, {level}");
    }
}

2.4 상속의 사용

record는 아래와 같이 상속을 사용할 수도 있다.

public record Monster
{
    public string name { get; init; }
    public int level { get; init; }
}

public record Dragon : Monster
{
    public int flySpeed { get; init; }
}

class Program
{
    static void Main(string[] args)
    {
        Monster p1 = new Dragon
        {
            name = "드래곤",
            level = 50,
            flySpeed = 30
        };
    }
}

3. record의 사용 이유

  1. 간결한 코드 작성 ( 가독성 향상 ) : 생성자 등 기본 메서드가 자동으로 생성되기 때문에 코드를 간결하고 가독성 좋게 작성할 수 있다.
  2. 불변성 보장 : record로 생성한 객체는 변경이 불가능하기 때문에 안정성이 보장된다.

참고자료

(record MSDN)
https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/types/records
(record)
https://sam0308.tistory.com/85

profile
게임 만들러 코딩 공부중

0개의 댓글