레코드 구조체
메모
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련LDM(언어 디자인 모임) 노트에서 캡처됩니다.
C# 언어 표준으로 기능 스펙릿을 채택하는 과정에 대한 자세한 내용은 사양문서에서 확인할 수 있습니다.
챔피언 이슈: https://github.com/dotnet/csharplang/issues/4334
레코드 구조체의 구문은 다음과 같습니다.
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct' identifier type_parameter_list?
parameter_list? struct_interfaces? type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body
| ';'
;
레코드 구조체 형식은 다른 구조체 형식과 같은 값 형식입니다.
System.ValueType
클래스에서 암시적으로 상속됩니다.
레코드 구조체의 한정자와 멤버는 구조체와 동일한 제한 사항이 적용됩니다(형식에 대한 접근성, 멤버의 한정자, base(...)
인스턴스 생성자 이니셜라이저, 생성자의 this
대한 명확한 할당, 소멸자, ...). 레코드 구조체는 매개 변수가 없는 인스턴스 생성자 및 필드 이니셜라이저에 대한 구조체와 동일한 규칙을 따르지만, 이 문서에서는 구조체에 대한 이러한 제한을 일반적으로 해제한다고 가정합니다.
§16.4.9매개 변수가 없는 구조체 생성자 사양을 참조하세요.
레코드 구조체는 ref
한정자를 사용할 수 없습니다.
부분 레코드 구조체의 하나 이상의 부분 형식 선언은 parameter_list
제공할 수 있습니다.
parameter_list
비어 있을 수 있습니다.
레코드 구조체 매개 변수는 ref
, out
또는 this
한정자를 사용할 수 없습니다(하지만 in
및 params
허용됨).
레코드 구조체의 멤버
레코드 구조체 본문에 선언된 멤버 외에도 레코드 구조체 형식에는 추가 합성 멤버가 있습니다. 레코드 구조체 본문에 "일치하는" 서명이 있는 멤버가 선언되어 있거나 접근 가능한 콘크리트 비 가상 멤버가 "일치하는" 서명으로 상속된 경우가 아니라면, 멤버는 합성됩니다. 두 멤버는 서명이 동일하거나 상속 시나리오에서 "숨기기"로 간주되는 경우 일치하는 것으로 간주됩니다. 서명 및 오버로드 §7.6참조하십시오. 레코드 구조체의 멤버 이름을 "Clone"으로 지정하는 것은 오류입니다.
레코드 구조체의 인스턴스 필드에 안전하지 않은 형식이 있는 것은 오류입니다.
레코드 구조체는 소멸자를 선언할 수 없습니다.
합성 멤버는 다음과 같습니다.
평등 회원
합성된 같음 멤버는 레코드 클래스와 유사합니다(이 형식의 경우Equals
, object
형식의 경우 Equals
, 이 형식의 경우 ==
및 !=
연산자).
EqualityContract
, null 검사 또는 상속이 없는 경우를 제외하고
레코드 구조체는 System.IEquatable<R>
구현하고 R
레코드 구조체인 Equals(R other)
강력한 형식의 합성 오버로드를 포함합니다.
메서드는 public
입니다.
메서드를 명시적으로 선언할 수 있습니다. 명시적 선언이 예상된 서명 또는 접근성과 일치하지 않는 경우 오류입니다.
Equals(R other)
사용자 정의(합성되지 않음)이지만 GetHashCode
아닌 경우 경고가 생성됩니다.
public readonly bool Equals(R other);
합성된 Equals(R)
레코드의 각 인스턴스 필드 fieldN
TN
필드 형식이 true
System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)
값을 구조화하는 경우에만 true
반환합니다.
레코드 구조체에는 다음과 같이 선언된 연산자와 동일한 합성된 ==
및 !=
연산자가 포함됩니다.
public static bool operator==(R r1, R r2)
=> r1.Equals(r2);
public static bool operator!=(R r1, R r2)
=> !(r1 == r2);
==
연산자가 호출하는 Equals
메서드는 위에서 지정한 Equals(R other)
메서드입니다.
!=
연산자는 ==
연산자에 대리합니다. 연산자가 명시적으로 선언된 경우 오류입니다.
레코드 구조체에는 다음과 같이 선언된 메서드에 해당하는 합성 재정의가 포함됩니다.
public override readonly bool Equals(object? obj);
재정의가 명시적으로 선언된 경우 오류입니다.
합성된 재정의는 other is R temp && Equals(temp)
을 반환하며, R
은 레코드 구조체입니다.
레코드 구조체에는 다음과 같이 선언된 메서드에 해당하는 합성 재정의가 포함됩니다.
public override readonly int GetHashCode();
메서드를 명시적으로 선언할 수 있습니다.
Equals(R)
및 GetHashCode()
중 하나가 명시적으로 선언되었지만 다른 메서드가 명시적이지 않은 경우 경고가 보고됩니다.
합성된 GetHashCode()
재정의는 각 인스턴스 필드 fieldN
에 대한 System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)
값을 결합하여 fieldN
형식인 TN
의 int
결과를 반환합니다.
예를 들어 다음 레코드 구조체를 고려합니다.
record struct R1(T1 P1, T2 P2);
이 레코드 구조체의 경우 합성된 같음 멤버는 다음과 같습니다.
struct R1 : IEquatable<R1>
{
public T1 P1 { get; set; }
public T2 P2 { get; set; }
public override bool Equals(object? obj) => obj is R1 temp && Equals(temp);
public bool Equals(R1 other)
{
return
EqualityComparer<T1>.Default.Equals(P1, other.P1) &&
EqualityComparer<T2>.Default.Equals(P2, other.P2);
}
public static bool operator==(R1 r1, R1 r2)
=> r1.Equals(r2);
public static bool operator!=(R1 r1, R1 r2)
=> !(r1 == r2);
public override int GetHashCode()
{
return Combine(
EqualityComparer<T1>.Default.GetHashCode(P1),
EqualityComparer<T2>.Default.GetHashCode(P2));
}
}
인쇄 멤버: PrintMembers 및 ToString 메서드
레코드 구조체에는 다음과 같이 선언된 메서드에 해당하는 합성된 메서드가 포함됩니다.
private bool PrintMembers(System.Text.StringBuilder builder);
메서드는 다음을 수행합니다.
- 각 레코드 구조체의 인쇄 가능한 멤버(비정적 공용 필드 및 읽을 수 있는 속성 멤버) 각각에 대해 해당 멤버의 이름 뒤에 " = "를 추가하고, 그 다음에 멤버의 값을 추가하며, 이러한 항목들을 ", "로 구분하여 연결합니다.
- 레코드 구조체에 인쇄 가능한 멤버가 있으면 true를 반환합니다.
값 형식이 있는 멤버의 경우 대상 플랫폼에서 사용할 수 있는 가장 효율적인 방법을 사용하여 해당 값을 문자열 표현으로 변환합니다. 현재는 ToString
을 호출한 후에 StringBuilder.Append
에 전달하는 것을 의미합니다.
레코드의 인쇄 가능한 멤버에 비readonly
get
접근자가 있는 읽기 가능한 속성이 포함되어 있지 않으면 합성된 PrintMembers
는 readonly
입니다.
PrintMembers
메서드가 readonly
가 되기 위해 레코드의 필드가 readonly
일 필요는 없습니다.
PrintMembers
메서드를 명시적으로 선언할 수 있습니다.
명시적 선언이 예상된 서명 또는 접근성과 일치하지 않는 경우 오류입니다.
레코드 구조체에는 다음과 같이 선언된 메서드에 해당하는 합성된 메서드가 포함됩니다.
public override string ToString();
레코드 구조체의 PrintMembers
메서드가 readonly
이면, 합성된 ToString()
메서드는 readonly
입니다.
메서드를 명시적으로 선언할 수 있습니다. 명시적 선언이 예상된 서명 또는 접근성과 일치하지 않는 경우 오류입니다.
합성된 메서드:
- 는
StringBuilder
인스턴스를 만듭니다. - 레코드 구조체 이름을 빌더 뒤에 추가한 다음 " { ",
- 레코드 구조체의
PrintMembers
메서드를 호출하여 작성기를 전달하고, true를 반환하면 뒤에 " "를 덧붙입니다. - "}"를 추가합니다.
- 작성기의 콘텐츠를
builder.ToString()
으로 반환합니다.
예를 들어 다음 레코드 구조체를 고려합니다.
record struct R1(T1 P1, T2 P2);
이 레코드 구조체의 경우 합성된 인쇄 멤버는 다음과 같습니다.
struct R1 : IEquatable<R1>
{
public T1 P1 { get; set; }
public T2 P2 { get; set; }
private bool PrintMembers(StringBuilder builder)
{
builder.Append(nameof(P1));
builder.Append(" = ");
builder.Append(this.P1); // or builder.Append(this.P1.ToString()); if P1 has a value type
builder.Append(", ");
builder.Append(nameof(P2));
builder.Append(" = ");
builder.Append(this.P2); // or builder.Append(this.P2.ToString()); if P2 has a value type
return true;
}
public override string ToString()
{
var builder = new StringBuilder();
builder.Append(nameof(R1));
builder.Append(" { ");
if (PrintMembers(builder))
builder.Append(" ");
builder.Append("}");
return builder.ToString();
}
}
위치 기록 구조체의 멤버
위의 멤버 외에도 매개 변수 목록("위치 레코드")이 있는 레코드 구조체는 위의 멤버와 동일한 조건으로 추가 멤버를 합성합니다.
기본 생성자
레코드 구조체에는 시그니처가 형식 선언의 값 매개 변수에 해당하는 공용 생성자가 있습니다. 이를 형식의 기본 생성자라고 부릅니다. 구조체에 이미 동일한 서명이 있는 기본 생성자와 생성자가 있는 것은 오류입니다. 형식 선언에 매개 변수 목록이 포함되어 있지 않으면 기본 생성자가 생성되지 않습니다.
record struct R1
{
public R1() { } // ok
}
record struct R2()
{
public R2() { } // error: 'R2' already defines constructor with same parameter types
}
레코드 구조체에 대한 인스턴스 필드 선언은 변수 이니셜라이저를 포함하도록 허용됩니다. 기본 생성자가 없는 경우 인스턴스 이니셜라이저는 매개 변수가 없는 생성자의 일부로 실행됩니다. 그렇지 않으면 런타임에 기본 생성자는 레코드 구조체 본문에 나타나는 인스턴스 이니셜라이저를 실행합니다.
레코드 구조체에 기본 생성자가 있는 경우 사용자 정의 생성자에는 기본 생성자 또는 명시적으로 선언된 생성자를 호출하는 명시적 this
생성자 이니셜라이저가 있어야 합니다.
레코드 구조체의 멤버뿐만 아니라 기본 생성자의 매개 변수는 인스턴스 필드 또는 속성의 이니셜라이저 내에서 범위에 있습니다. 인스턴스 멤버는 이러한 위치에서 사용할 경우 오류가 발생하지만(마치 인스턴스 멤버가 일반 생성자 이니셜라이저의 범위 안에 있지만 사용하는 것이 오류인 것과 같음) 주 생성자의 매개 변수는 범위 안에 있으며 사용 가능하며 멤버를 가립니다. 정적 멤버도 사용할 수 있습니다.
기본 생성자의 매개 변수를 읽지 않으면 경고가 생성됩니다.
구조체 인스턴스 생성자에 대한 명확한 할당 규칙은 레코드 구조체의 기본 생성자에 적용됩니다. 예를 들어 다음 오류는 다음과 같습니다.
record struct Pos(int X) // definite assignment error in primary constructor
{
private int x;
public int X { get { return x; } set { x = value; } } = X;
}
속성
레코드 구조체 선언의 각 레코드 구조체 매개 변수에는 값 매개 변수 선언에서 이름과 형식을 가져온 해당 public 속성 멤버가 있습니다.
레코드 구조체의 경우:
- 레코드 구조체에
readonly
한정자가 있으면 공용get
및init
자동 속성이 만들어지며, 그렇지 않으면get
및set
자동 속성이 생성됩니다. 두 종류의 집합 접근자(set
및init
)는 모두 "일치"로 간주됩니다. 따라서 사용자는 합성된 변경 가능한 속성 대신 init 전용 속성을 선언할 수 있습니다. 형식이 일치하는 상속된abstract
속성이 재정의됩니다. 레코드 구조체에 예상 이름과 형식이 있는 인스턴스 필드가 있는 경우 자동 속성이 만들어지지 않습니다. 상속된 속성에public
get
및set
/init
접근자가 없는 경우 오류가 발생합니다. 상속된 속성 또는 필드를 숨기면 오류가 발생합니다.
자동 속성은 해당 기본 생성자 매개 변수의 값으로 초기화됩니다. 특성은 해당 레코드 구조체 매개 변수에 구문적으로 적용된 특성을property:
또는field:
대상으로 사용하여 합성된 자동 속성 및 해당 지원 필드에 적용할 수 있습니다.
분해
하나 이상의 매개변수가 포함된 위치 레코드 구조체는 각 매개변수에 대해 기본 생성자 선언의 out 매개변수 선언과 함께 Deconstruct
라는 이름의 public void 반환 인스턴스 메서드를 합성합니다. Deconstruct 메서드의 각 매개 변수에는 기본 생성자 선언의 해당 매개 변수와 동일한 형식이 있습니다. 메서드 본문은 Deconstruct 메서드의 각 매개 변수를 인스턴스 멤버 액세스에서 동일한 이름의 멤버에 대한 값에 할당합니다.
인스턴스 멤버가 본문에서 액세스되지만 비readonly
get
접근자 속성을 포함하지 않을 경우, 합성된 Deconstruct
메서드는 readonly
입니다.
메서드를 명시적으로 선언할 수 있습니다. 명시적 선언이 예상된 서명 또는 접근성과 일치하지 않거나 정적이면 오류가 발생합니다.
구조체에서 with
식 허용
이제 with
식의 수신기에 구조체 형식이 있는 것이 유효합니다.
with
식의 오른쪽에는 수신 객체의 유형의 액세스 가능한 인스턴스 필드나 속성이어야 하는 식별자를 포함하는 할당 순서가 있는 member_initializer_list
이 있습니다.
구조체 형식의 수신기의 경우 수신기가 먼저 복사된 다음 각 member_initializer
변환 결과의 필드 또는 속성 액세스에 대한 할당과 동일한 방식으로 처리됩니다.
할당은 어휘 순서로 처리됩니다.
레코드 개선 사항
허용 record class
레코드 형식에 대한 기존 구문을 사용하면 record class
를 record
과 동일한 의미로 사용할 수 있습니다.
record_declaration
: attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
parameter_list? record_base? type_parameter_constraints_clause* record_body
;
사용자 정의 위치 멤버를 필드로 허용
레코드에 예상된 이름과 형식의 인스턴스 필드가 있거나 상속되는 경우 자동 속성이 만들어지지 않습니다.
구조체에서 매개 변수가 없는 생성자 및 멤버 이니셜라이저 허용
매개 변수 없는 구조체 생성자 사양을 참조하세요.
질문 열기
- 메타데이터에서 레코드 구조체를 인식하는 방법 (활용할 수 있는 말할 수 없는 클론 메서드가 없습니다...)
응답 완료
- PrintMembers 디자인(
bool
반환하는 별도의 메서드)을 유지하려는지 확인합니다(대답: 예). -
record ref struct
허용하지 않는지 확인(IEquatable<RefStruct>
및 ref 필드 관련 문제)(답변: 예) - 동등성 멤버의 구현을 확인합니다. 합성된
bool Equals(R other)
,bool Equals(object? other)
및 연산자가 모두ValueType.Equals
에 위임하는 대안이 있습니다. (대답: 예) - 기본 생성자가 있을 때 필드 이니셜라이저를 허용하려는지 확인합니다. 또한 매개 변수가 없는 구조체 생성자를 허용하려고 합니까(활성화기 문제가 분명히 해결됨)? (답변: 예, 업데이트된 사양은 LDM에서 검토해야 함)
-
Combine
방법에 대해 얼마나 말하고 싶습니까? (답변: 가능한 한 적음) - 복사 생성자 서명을 사용하여 사용자 정의 생성자를 허용하지 않아야 하나요? (대답: 아니요, 레코드 구조체 사양에는 복사 생성자의 개념이 없습니다.)
- "Clone"이라는 이름의 멤버를 허용하지 않도록 한다는 것을 확인합니다. (답변: 정답)
- 합성된
Equals
논리가 런타임 구현(예: float)에 기능적으로 동일한지 다시 확인합니다. NaN)(답변: LDM에서 확인됨) - 필드 또는 속성 대상 지정 특성을 위치 매개 변수 목록에 배치할 수 있나요? (대답: 예, 레코드 클래스와 동일)
- 제네릭에 대한
with
코드? (답변: C# 10의 범위를 벗어났습니다.) -
record struct S1;
과record struct S2;
에서 다른 값을 얻으려면GetHashCode
에 형식 자체의 해시를 포함해야 할까요? (답변: 아니요)
C# feature specifications