Record
2022. 1. 21. 00:00ㆍCSharp/Advance
반응형
C# 9부터 record 키워드를 사용하여 데이터를 캡슐화하는 기본 제공 기능을 제공하는 참조 형식을 정의합니다. 위치 매개 변수 또는 표준 속성 구문을 사용하여 변경할 수 없는 속성이 있는 레코드 형식을 만들 수 있습니다.
// Record 선언 (간단한 방식)
public record Person(string FirstName, string LastName);
// Record 선언 다른 방식
//public record Person
//{
// public string FirstName { get; init; } = default!;
// public string LastName { get; init; } = default!;
//};
변경할 수 있는 속성 및 필드를 사용하여 레코드 형식을 만들 수도 있다.
public record Person
{
public string FirstName { get; set; } = default!;
public string LastName { get; set; } = default!;
};
C# 10 이상에서는 위치 매개 변수 또는 표준 속성 구문을 사용하여 record struct 형식을 정의할 수 있다.
// struct 도 record 로 만들 수 있다.
// readonly 속성을 통해 변경 불가능 하다.
public readonly record struct Point(double X, double Y, double Z);
// set 을 init 으로 선언하여 변경 불가능 하게 만든다.
//public record struct Point
//{
// public double X { get; init; }
// public double Y { get; init; }
// public double Z { get; init; }
//}
// 변경 가능한 record struct
public record struct Point(double X, double Y, double Z);
//public record struct Point
//{
// public double X { get; set; }
// public double Y { get; set; }
// public double Z { get; set; }
//}
이전 예제는 참조 형식인 레코드와 값 형식인 레코드 간의 몇 가지 차이점을 보여준다.
- record 또는 record class는 참조 형식을 선언.
- class 키워드는 선택 사항이지만 읽는 사람에게 명확성을 줄 수 있다.
- record struct는 값 형식
- 위치 속성은 record class 및 readonly record struct에서 ‘변경 불가능’. record struct에서는 ‘변경 가능’.
위의 내용을 record class 로 선언하면..
// 변경 불가능 record class (readonly 가 없어도 기본적으로 불가능)
public record class Point(double X, double Y, double Z);
// class 는 생략 가능
//public record Point(double X, double Y, double Z);
record 에 attribute 를 추가해서 선언 할 수도 있다.
/// <summary>
/// Person record type
/// </summary>
/// <param name="FirstName">First Name</param>
/// <param name="LastName">Last Name</param>
/// <remarks>
/// The person type is a positional record containing the
/// properties for the first and last name. Those properties
/// map to the JSON elements "firstName" and "lastName" when
/// serialized or deserialized.
/// </remarks>
public record Person([property: JsonPropertyName("firstName")]string FirstName,
[property: JsonPropertyName("lastName")]string LastName);
생성된 자동 구현 속성 정의가 원하는 내용이 아니면 동일한 이름의 속성을 직접 정의할 수 있다.
public record Person(string FirstName, string LastName, string Id)
{
internal string Id { get; init; } = Id;
}
불변성
초기화 후에는 값 형식 속성의 값 또는 참조 형식 속성의 참조를 변경할 수 없다.
그러나 참조 형식 속성이 참조하는 데이터는 변경할 수 있다.
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static void Main()
{
Person person = new("Nancy", "Davolio", new string[1] { "555-1234" });
Console.WriteLine(person.PhoneNumbers[0]); // output: 555-1234
//person.FirstName = "1234"; // 이것은 변경 불가 Compile error
//person.LastName = "1234"; // 이것은 변경 불가 Compile error
person.PhoneNumbers[0] = "555-6789"; // 이것은 변경 가능 (참조 형식 속성이 참조하는 데이터)
Console.WriteLine(person.PhoneNumbers[0]); // output: 555-6789
}
값 같음
- class 형식의 경우 두 개체가 메모리에서 동일한 개체를 참조하면 두 개체는 동일
- struct 형식의 경우 두 개체가 동일한 형식을 갖고 동일한 값을 저장하면 두 개체는 동일
- record struct 및 readonly record struct를 포함하는 record 형식의 경우 두 개체가 동일한 형식을 갖고 동일한 값을 저장하면 두 개체는 동일
다음 예제에서는 레코드 형식의 값 같음을 보여준다.
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static void Main()
{
var phoneNumbers = new string[2];
Person person1 = new("Nancy", "Davolio", phoneNumbers);
Person person2 = new("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // output: True
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True
Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
}
Nondestructive mutation
With 이용하여 기존 레코드 인스턴스의 지정된 속성 및 필드가 수정된 복사본인 새 레코드 인스턴스를 만듭니다.
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; }
}
public static void Main()
{
Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
Console.WriteLine(person1);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Person person2 = person1 with { FirstName = "John" };
Console.WriteLine(person2);
// output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { PhoneNumbers = new string[1] };
Console.WriteLine(person2);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { };
Console.WriteLine(person1 == person2); // output: True
}
상속
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}
파생 레코드의 with 식
public record Point(int X, int Y)
{
public int Zbase { get; set; }
};
public record NamedPoint(string Name, int X, int Y) : Point(X, Y)
{
public int Zderived { get; set; }
};
public static void Main()
{
Point p1 = new NamedPoint("A", 1, 2) { Zbase = 3, Zderived = 4 };
Point p2 = p1 with { X = 5, Y = 6, Zbase = 7 }; // Can't set Name or Zderived
Console.WriteLine(p2 is NamedPoint); // output: True
Console.WriteLine(p2);
// output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = A, Zderived = 4 }
Point p3 = (NamedPoint)p1 with { Name = "B", X = 5, Y = 6, Zbase = 7, Zderived = 8 };
Console.WriteLine(p3);
// output: NamedPoint { X = 5, Y = 6, Zbase = 7, Name = B, Zderived = 8 }
}
파생 레코드의 분해자 동작
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
var (firstName, lastName) = teacher; // Doesn't deconstruct Grade
Console.WriteLine($"{firstName}, {lastName}");// output: Nancy, Davolio
var (fName, lName, grade) = (Teacher)teacher;
Console.WriteLine($"{fName}, {lName}, {grade}");// output: Nancy, Davolio, 3
}
관련영상
반응형
'CSharp > Advance' 카테고리의 다른 글
제네릭 클래스 (Generic class) (0) | 2022.01.25 |
---|---|
제네릭 컬렉션 (Generic Collection) (0) | 2022.01.24 |
Lambda advanced (0) | 2022.01.20 |
Action, Func, Lambda (0) | 2022.01.19 |
Event (이벤트) (0) | 2022.01.18 |