1. Introduction
Record
형식은 C# 9 (.NET 5) 에서 소개된Immutable reference type
을 목적으로 설계된 형식이다.Record
형식은 기본적으로class
형식과 비슷하다. 형식 사용 방법 또한 상당히 유사하다.- 다른 점으로는
immutable
지원을 위한 기능이 추가되어 최소한의 코드로immutable reference type
을 구현할 수 있다. - C# 10 (.NET 6) 에서
record struct
가 소개되면서 값 형식의 사용이 가능해지고, 명시적으로record class
선언을 할 수 있게 되었다.
2. Record의 장점
Record
형식은 다음과 같은 장점을 가진다.Immutable reference type
- Constructor & Deconstruct 자동 지원
- Positional record 구현 시
- with식 지원
Equals(object)
,==
&!=
지원- 이 부분은
class
와 다른 부분이다. 형식 및 값, 참조 검사가 지원된다.
- 이 부분은
- PrintMembers &
ToString()
자동 지원 - class와 같이 상속을 지원한다.
3. Record 정의
3.1. Record 기본 정의
record
또는record class
형식으로 정의한다.
public record CartesianCoordinate
{
public double X { get; set; }
public double Y { get; set; }
}
- 상기 정의에서는 property setter 키워드를 일부러
set
으로 설정하였다.
이런 경우 아래와 같이 프로퍼티 값을 변경하는 것이 가능하다.
var position = new CartesianCoordinate{
X = 1,
Y = 2
};
position.X = 5;
- 상기와 같이 정의를 하고 사용하는 경우
class
와 차이가 없다. (일부러 record를 사용할 필요가 없다)
따라서immutable
을 위해 다음과 같이 정의하도록 한다.
3.2. Immutable record 정의
public record CartesianCoordinate
{
public double X { get; init; } // init 키워드 : C# 9
public double Y { get; init; }
}
- 초기화 시에만 값을 할당하게 해주는 init 키워드를 통해
record
가 불변성을 갖게 되었다. - 다음 절을 통해,
record
의 구체적인 정의 및 사용법을 알아본다.
4. Positional record 정의
4.1. Positional record, class 정의
Positional record
는 다음과 같이 메서드와 비슷한 형태로 정의한다.
public record CartesianCoordinate(double X, double Y); // X, Y에 대해 컴파일러가 프로퍼티 (get; init;) 를 구성해준다.
- 이와 유사한 기능을 할 수 있는
class
를 구현하면 아래와 같이 구현된다.
public class CartesianCoordinate
{
public double X { get; init; }
public double Y { get; init; }
public CartesianCoordinate(double x, double y)
{
X = x;
Y = y;
}
public void Deconstruct(out double x, out double y)
{
x = X;
y = Y;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(nameof(CartesianCoordinate));
builder.Append(" { ");
if (PrintMembers(builder))
{
builder.Append(" ");
}
builder.Append("}");
return builder.ToString();
}
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append($"X = {X}, ");
builder.Append($"Y = {Y}");
return true;
}
}
4.2. Positional record, class 비교
4.1. 내용을 통해 우리는 컴파일러가
record
, 특히positional record
를 위해 생성해주는 코드를 알 수 있다.- 파라미터에 맞는 프로퍼티를 생성한다. (Public 속성이다)
- 파라미터에 맞는 생성자를 만든다.
- 파라미터에 맞는
Deconstruct
메서드를 정의해준다. Record
명과 프로퍼티를 적절하게 출력할 수 있는 ToString()을 정의해준다.Record
공통
이는 우리가 작성해야 할 코드의 양이 획기적으로 줄어드는 것을 의미한다.
특히 상속이 이루어지는 경우,
positional record
의 효율성은 극대화된다.public record CartesianCoordinate(double X, double Y); public record CartesianCoordinate3D(double X, double Y, double Z) : CartesianCoordinate(X, Y);
따라서 불변성을 가지는 참조 형식의 데이터가 필요한 경우,
Positional record
가 좋은 선택지가 될 수 있다.
4.3. Positional parameter 재정의 및 프로퍼티 생성
Positional record
의 자동 구현 프로퍼티가 마음에 들지 않는 경우 같은 이름으로 프로퍼티를 정의할 수 있다.Positional parameter
외에 추가로 프로퍼티를 생성할 수도 있다.
public record CartesianCoordinate(double X)
{
internal double X { get; init; } = X; // 같은 이름의 프로퍼티 정의
public double Y { get; init; } = 0; // 프로퍼티 생성
}
5. Positional record 사용
Positional record
는 다음과 같이 사용한다.
public record CartesianCoordinate(double X, double Y);
CartesianCoordinate coordinate = new(1, 2);
// record 출력
Console.WriteLine(coordinate.X); // 1
Console.WriteLine(coordinate.Y); // 2
Console.WriteLine(coordinate); // CartesianCoordinate { X = 1, Y = 2 }
// Deconstruct
var (X, Y) = coordinate;
// with 식 사용
CartesianCoordinate coordinate1 = coordinate with { X = 3 };
Console.WriteLine(coordinate1); // CartesianCoordinate { X = 3, Y = 2 }
// 객체 비교
coordinate1 = coordinate with {};
Console.WriteLine(coordinate == coordinate1); // True
Console.WriteLine(object.ReferenceEquals(coordinate, coordinate1)); // False
6. record struct
- C# 10에서
record struct
타입이 새로 추가되었다. - 기존의
record
(record class
) 는 참조 형식,record struct
형식은 값 형식이라는 데 차이가 있다. Positional record
선언 시record class
는 프로퍼티가{ get; init; }
으로 생성되지만record struct
는{ get; set; }
으로 생성되어 값의 수정이 가능하다.// public record CartesianCoordinate(float X, float Y); public record struct CartesianCoordinate(float X, float Y);
/* record class는 setter가 init으로 생성된다. public record CartesianCoordinate { public float X { get; init; } public float Y { get; init; } } */ public record struct CartesianCoordinate { public float X { get; set; } public float Y { get; set; } }
7. 주의사항
- 불변성을 지닌
record
를 정의하더라도, 주의해야 할 사항이 있다.- 파라미터에 대한 완전한 불변성은
값 형식
에 대해서만 한정된다. 참조 형식
파라미터의 경우,참조
에 대해서만 불변성이 보장되고 데이터는 변경될 수 있다.
- 파라미터에 대한 완전한 불변성은
public record CargoList(string ContainerName, List<string> Items);
CargoList cargo = new("My Container", new List<string>() { "Stone", "Fish" });
Console.WriteLine(cargo); // CargoList { ContainerName = My Container, Items = System.Collections.Generic.List`1[System.String] }
Console.WriteLine(cargo.Items[0]); // Stone
cargo.Items[0] = "TV";
Console.WriteLine(cargo.Items[0]); // TV