메모
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련된 언어 디자인 모임(LDM) 노트에 기록되어 있습니다.
C# 언어 표준에 기능 스펙렛을 채택하는 과정에 대해 사양 문서에서 더 자세히 알아볼 수 있습니다.
챔피언 발행호: https://github.com/dotnet/csharplang/issues/9662
요약
공용 구조체 는 통합 형식에 대한 C# 지원을 제공하기 위해 결합되는 상호 연결된 기능 집합입니다.
-
공용 구조체 형식: 특성이 있는
[Union]구조체 및 클래스는 공용 구조체 형식으로 인식되며 공용 구조체 동작을 지원합니다. - 사례 형식: 공용 구조체 형식에는 생성자 및 팩터리 메서드에 대한 매개 변수에 의해 제공되는 사례 형식 집합이 있습니다.
-
공용 구조체 동작: 공용 구조체 유형은 다음 공용 구조체 동작을 지원합니다.
- 공용 구조체 변환: 각 사례 형식에서 공용 구조체 형식으로의 암시적 공용 구조체 변환 이 있습니다.
- 공용 구조체 일치: 공용 구조체 값에 대한 패턴 일치는 콘텐츠를 암시적으로 "래핑 해제"하고 대신 기본 값에 패턴을 적용합니다.
- 공용 구조체 완전성: 대체 사례 없이 모든 사례 형식이 일치하면 통합 값에 대한 스위치 식이 완전합니다.
- 공용 구조체 null 허용 여부: Null 허용 여부 분석을 통해 공용 구조체 내용의 null 상태를 추적할 수 있습니다.
- 공용 구조체 패턴: 모든 공용 구조체 형식은 기본 공용 구조체 패턴을 따르지만 특정 시나리오에 대한 추가 선택적 패턴이 있습니다.
- 공용 구조체 선언: 약식 구문을 사용하면 공용 구조체 형식을 직접 선언할 수 있습니다. 구현은 기본 공용 구조체 패턴을 따르고 내용을 단일 참조 필드로 저장하는 구조체 선언인 "opinionated"입니다.
- 공용 구조체 인터페이스: 몇 가지 인터페이스는 언어로 알려져 있으며 공용 구조체 선언의 구현에 사용됩니다.
동기
공용 구조체는 오랫동안 요청된 C# 기능으로, 패턴 일치가 완전하게 신뢰할 수 있는 방식으로 닫힌 형식 집합의 값을 표현할 수 있습니다.
공용 구조 체 형식 과 공용 구조체 선언 을 분리하면 C#에서 의견이 있는 의미 체계가 포함된 간결한 공용 구조체 선언 구문을 사용할 수 있을 뿐만 아니라 다른 구현 선택 사항이 있는 기존 형식이나 형식이 공용 구조체 동작을 옵트인할 수 있습니다.
C#에서 제안된 공용 구조체는 형식 의 공용 구조체이며 "차별" 또는 "태그 지정"이 아닙니다. "차별된 공용 구조체"는 새 형식 선언을 대/소문자 형식으로 사용하여 "형식 공용 구조체"의 관점에서 표현할 수 있습니다. 또는 폐쇄형 계층 구조로 구현할 수 있으며, 이는 완전성에 초점을 맞춘 또 다른 관련 곧 출시될 C# 기능입니다.
상세 디자인
유니언 타입
특성이 있는 System.Runtime.CompilerServices.UnionAttribute 모든 클래스 또는 구조체 형식은 공용 구조체 형식으로 간주됩니다.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(Class | Struct, AllowMultiple = false)]
public class UnionAttribute : Attribute;
}
공용 구조체 형식은 공용 공용 조합원의 특정 패턴을 따라야 하며, 이 패턴은 공용 조합 유형 자체에 선언되거나 "공용 구조체 구성원 공급자"에게 위임되어야 합니다.
일부 조합원은 필수이며 다른 조합원은 선택 사항입니다.
공용 구조체 형식에는 특정 조합원의 서명에 따라 설정된 사례 형식 집합이 있습니다.
공용 구조체 값의 내용은 속성을 통해 Value 액세스할 수 있습니다. 언어는 사례 형식 중 하나 또는 null 값만 포함한다고 가정 Value합니다(올바른 형식 참조).
공용 구조체 멤버 공급자
기본적으로 공용 구조체 멤버는 공용 구조체 형식 자체에서 찾을 수 있습니다. 그러나 공용 구조체 형식에 호출 IUnionMembers 된 인터페이스 선언이 직접 포함된 경우 인터페이스는 공용 구조체 멤버 공급자 역할을 합니다. 이 경우 조합원은 노조 유형 자체가 아닌 조합원 공급자에서 만 찾을 수 있습니다.
공용 멤버 공급자 인터페이스는 공용이어야 하며 공용 형식 자체는 인터페이스로 구현해야 합니다.
공용 구조체 멤버가 있는 형식에 대해 공용 구조체 정의 형식이라는 용어를 사용합니다. 이 형식이 있으면 공용 구조체 멤버 공급자이고, 그렇지 않으면 공용 구조체 형식 자체를 사용합니다.
공용 구조체 구성원
공용 구조체 구성원은 공용 구조체 정의 형식에서 이름과 서명으로 조회됩니다. 공용 구조체 정의 형식에서 직접 선언할 필요는 없지만 상속될 수 있습니다.
모든 조합원이 공개되지 않는 것은 오류입니다.
생성 멤버와 Value 속성은 필수이며 기본 공용 구조체 패턴이라고도 합니다.
멤버와 TryGetValue 멤버를 HasValue 통칭하여 비복싱 공용 구조체 액세스 패턴이라고 합니다.
다른 공용 구조체 구성원은 다음에 설명되어 있습니다.
공용 구조체 만들기 멤버
공용 구조체 만들기 멤버는 사례 형식 값에서 새 공용 구조체 값을 만드는 데 사용됩니다.
공용 구조체 정의 형식이 공용 구조체 형식 자체인 경우 단일 매개 변수가 있는 각 생성 자는 공용 구조체 생성자입니다. 공용 구조체의 사례 형식은 다음과 같은 방법으로 이러한 생성자의 매개 변수 형식에서 빌드된 형식 집합으로 식별됩니다.
- 매개 변수 형식이 nullable 형식(값 또는 참조)인 경우 사례 형식은 기본 형식입니다.
- 그렇지 않으면 대/소문자 형식이 매개 변수 형식입니다.
// Union constructor making `Dog` a case type
public Pet(Dog value) { ... }
// Union constructor making `int` a case type
public Union(int? value) { ... }
// Union constructor making `string` a case type
public Union(string? value) { ... }
공용 구조체 정의 형식이 공용 구조체 멤버 공급자인 경우 단일 매개 변수가 있는 각 정적 Create 메서드와 공용 구조체 형식 자체로 ID로 변환할 수 있는 반환 형식은 공용 구조체 팩터리 메서드입니다.
공용 구조체의 사례 형식은 다음과 같은 방법으로 이러한 팩터리 메서드의 매개 변수 형식에서 빌드된 형식 집합으로 식별됩니다.
- 매개 변수 형식이 nullable 형식(값 또는 참조)인 경우 사례 형식은 기본 형식입니다.
- 그렇지 않으면 대/소문자 형식이 매개 변수 형식입니다.
// Union factory method making `Cat` a case type
public static Pet Create(Cat value) { ... }
// Union factory method making `int` a case type
public static Union Create(int? value) { ... }
// Union factory method making `string` a case type
public static Union Create(string? value) { ... }
공용 구조체 생성자 및 공용 구조체 팩터리 메서드를 공용 구조체 생성 멤버라고 합니다.
공용 구조체 만들기 멤버의 단일 매개 변수는 값 또는 in 매개 변수여야 합니다.
공용 구조체 형식에는 하나 이상의 공용 구조체 생성 멤버가 있어야 하므로 하나 이상의 사례 형식이 있어야 합니다.
Value 속성
이 속성은 Value 대/소문자 유형에 관계없이 공용 구조체에 포함된 값에 액세스할 수 있습니다.
모든 공용 구조체 정의 형식은 형식 object? 또는 object.의 속성을 선언 Value 해야 합니다. 속성에는 접근자가 get 있어야 하며 선택적으로 initset 접근성이 있을 수 있고 컴파일러에서 사용되지 않는 접근자가 있을 수 있습니다.
// Union 'Value' property
public object? Value { get; }
비복싱 액세스 멤버
공용 구조체 형식은 각 사례 유형에 대해 강력한 형식의 조건부 액세스를 허용하는 비복싱 공용 구조체 액세스 패턴을 추가로 구현하고 null을 확인하는 방법을 선택할 수 있습니다.
이렇게 하면 사례 형식이 값 형식이고 공용 구조체 내에 저장될 때 컴파일러가 패턴 일치를 보다 효율적으로 구현할 수 있습니다.
boxing이 아닌 액세스 멤버는 다음과 같습니다.
-
HasValuepublicget접근자가 있는 형식bool의 속성입니다. 필요에 따라init접근성이 있을 수 있고 컴파일러에서 사용되지 않는 접근자가 있을set수 있습니다. -
TryGetValue각 사례 형식에 대한 메서드입니다. 메서드는 ID 변환 가능한 형식의 단일 out 매개 변수를 반환bool하고 대/소문자 형식으로 가져옵니다.
// Non-boxing access members
public bool HasValue { get { ... } }
public bool TryGetValue(out Dog value) { ... }
HasValue 는 공용 구조체 Value 가 null이 아닌 경우에만 true를 반환해야 합니다.
TryGetValue 는 공용 구조체가 Value 지정된 사례 형식인 경우에만 true를 반환하고, 그럴 경우 메서드의 out 매개 변수에 해당 값을 전달합니다.
정확한 형식화
언어 및 컴파일러는 공용 구조체 형식에 대해 여러 가지 동작을 가정합니다. 형식이 공용 구조체 형식으로 자격이 있지만 이러한 가정을 충족하지 않는 경우 공용 구조체 동작이 예상대로 작동하지 않을 수 있습니다.
-
소리: 속성은
Value항상 null 또는 사례 유형의 값으로 계산됩니다. 공용 구조체 형식의 기본값에도 마찬가지입니다. -
안정성: 사례 형식
Value에서 공용 구조체 값을 만드는 경우 속성은 해당 사례 형식 또는 null과 일치합니다. 값에서null공용 구조체 값을 만들면 속성이Value됩니다null. - 생성 동등성: 값이 암시적으로 두 개의 다른 사례 형식으로 변환할 수 있는 경우 해당 사례 형식 중 하나에 대한 생성 멤버는 해당 값으로 호출할 때 동일한 관찰 가능한 동작을 갖습니다.
-
액세스 패턴 일관성: boxing이 아닌 액세스 멤버의
HasValue동작(있는 경우)은 속성에 대해Value직접 확인하는 동작과TryGetValue동일합니다.
공용 구조체 형식의 예
Pet 는 공용 구조체 형식 자체에 대한 기본 공용 구조체 패턴을 구현합니다.
[Union] public record struct Pet
{
// Creation members = case types are 'Dog' and 'Cat'
public Pet(Dog value) => Value = value;
public Pet(Cat value) => Value = value;
// 'Value' property
public object? Value { get; }
}
IntOrBool 는 공용 구조체 형식 자체에 대해 boxing이 아닌 액세스 패턴을 구현합니다.
public record struct IntOrBool
{
private bool _isBool;
private int _value;
public IntOrBool(int value) => (_isBool, _value) = (false, value);
public IntOrBool(bool value) => (_isBool, _value) = (true, value ? 1 : 0);
public object Value => _isBool ? _value is 1 : _value;
public bool HasValue => true;
public bool TryGetValue(out int value)
{
value = _value;
return !_isBool;
}
public bool TryGetValue(out bool value)
{
value = _isBool && _value is 1;
return _isBool;
}
}
참고: 이는 boxing이 아닌 액세스 패턴을 구현하는 방법의 예일 뿐입니다. 사용자 코드는 콘텐츠를 좋아하는 방식으로 저장할 수 있습니다. 특히, 구현이 boxing에서 방지하지 않습니다!
non-boxing 이름에서 컴파일러의 패턴 일치 구현이 -typed Value 속성과 달리 강력한 형식의 방식으로 각 사례 형식에 액세스할 수 있도록 하는 것을 object?의미합니다.
Result<T> 는 공용 구조체 멤버 공급자를 통해 기본 패턴을 구현합니다.
public record class Result<T> : Result<T>.IUnionMembers
{
object? _value;
public interface IUnionMembers
{
public static Result<T> Create(T value) => new() { _value = value };
public static Result<T> Create(Exception value) => new() { _value = value };
public object? Value { get; }
}
object? IUnionMembers.Value => _value;
}
공용 구조체 동작
공용 구조체 동작은 일반적으로 기본 공용 구조체 패턴을 통해 구현됩니다. 공용 구조체가 비복싱 액세스 패턴을 제공하는 경우 공용 구조체 패턴 일치가 우선적으로 사용됩니다.
공용 구조체 변환
공용 구조체 변환은 각 사례 형식에서 공용 구조체 형식으로 암시적으로 변환됩니다. 특히 형식으로의 표준 암시적 변환이 있고 공용 구조체 생성 멤버의 매개 변수 형식 U 인 경우 형식 또는 식 E 에서 E공용 구조체 형식 CC 으로의 U공용 구조체 변환이 있습니다.
공용 구조체 형식 U 이 구조체인 경우 형식으로 U? 의 표준 암시적 변환이 있고 공용 구조체 생성 멤버의 매개 변수 형식인 경우 형식 또는 식 E 에서 E 형식 CC 으로의 U공용 구조체 변환이 있습니다.
공용 구조체 변환 자체는 표준 암시적 변환이 아닙니다. 따라서 사용자 정의 암시적 변환 또는 다른 공용 구조체 변환에 참여하지 않을 수 있습니다.
암시적 공용 구조체 변환 이외의 명시적 공용 구조체 변환은 없습니다. 따라서 공용 구조체의 사례 형식으로 E 의 명시적 변환이 있더라도 해당 공용 구조체 형식 C으로 E 의 명시적 변환이 있는 것은 아닙니다.
공용 구조체 변환은 공용 구조체의 생성 멤버를 호출하여 실행됩니다.
Pet pet = dog;
// becomes
Pet pet = new Pet(dog);
// and
Result<string> result = "Hello"
//becomes
Result<string> result = Result<string>.IUnionMembers.Create("Hello");
오버로드 확인에서 단일 최고의 후보 멤버를 찾지 못하거나 해당 멤버가 노조 유형의 조합원 중 하나가 아닌 경우 오류가 발생합니다.
공용 구조체 변환은 암시적 사용자 정의 변환의 또 다른 "양식"입니다. 적용 가능한 사용자 정의 변환 연산자 "shadows" 공용 구조체 변환입니다.
이 결정의 근거는 다음과 같습니다.
사용자가 사용자 정의 연산자를 작성한 경우 우선 순위가 지정됩니다. 즉, 사용자가 실제로 자신의 연산자를 작성한 경우 사용자가 해당 연산자를 호출하기를 원합니다. 변환 연산자가 공용 구조체 형식으로 변환된 기존 형식은 현재 연산자를 활용하는 기존 코드와 동일한 방식으로 계속 작동합니다.
다음 예제에서는 암시적 사용자 정의 변환이 공용 구조체 변환보다 우선합니다.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => ...
public S1(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static implicit operator S1(int x) => ...
}
class Program
{
static S1 Test1() => 10; // implicit operator S1(int x) is used
static S1 Test2() => (S1)20; // implicit operator S1(int x) is used
}
다음 예제에서는 코드에서 명시적 캐스트를 사용하는 경우 명시적 사용자 정의 변환이 공용 구조체 변환보다 우선합니다. 그러나 코드에 명시적 캐스트가 없는 경우 명시적 사용자 정의 변환을 적용할 수 없으므로 공용 구조체 변환이 사용됩니다.
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => ...
public S2(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static explicit operator S2(int x) => ...
}
class Program
{
static S2 Test3() => 10; // Union conversion S2.S2(int) is used
static S2 Test4() => (S2)20; // explicit operator S2(int x)
}
공용 구조체 일치
패턴의 들어오는 값이 공용 구조체 형식이거나 공용 구조체 형식의 nullable인 경우 패턴에 따라 nullable 값과 기본 공용 구조체 값의 내용이 "래핑 해제"될 수 있습니다.
무조건 _ 및 var 패턴의 경우 패턴이 들어오는 값 자체에 적용됩니다. 다음은 그 예입니다.
if (GetPet() is var pet) { ... } // 'pet' is the union value returned from `GetPet`
그러나 다른 모든 패턴은 기본 공용 구조체의 Value 속성에 암시적으로 적용됩니다.
if (GetPet() is Dog dog) { ... } // 'Dog dog' is applied to 'GetPet().Value'
if (GetPet() is null) { ... } // 'null' is applied to 'GetPet().Value'
if (GetPet() is { } value) { ... } // '{ } value' is applied to 'GetPet().Value'
논리 패턴의 경우 이 규칙은 패턴의 왼쪽 분기가 오른쪽 분기의 and 들어오는 형식에 영향을 줄 수 있음을 염두에 두고 분기에 개별적으로 적용됩니다.
GetPet() switch
{
var pet and not null => ... // 'var pet' applies to the incoming 'Pet' and 'not null' to its 'Value'
not null and var value => ... // 'not null' applies to the 'Value' as does 'var value' because of the
// left branch changing the incoming type to `object?`.
}
참고: 이 규칙은 콘텐츠에 적용된 대로 Pet 공용 구조체 자체가 아니라 성공할 가능성이 높다는 Pet 것을 의미 GetPet() is Pet pet 합니다.
참고: 무조건적인 var 패턴의 다른 처리에 대 한 이유 (뿐만 _아니라, 에 대 한 var _본질적으로 약어) 그들의 사용은 다른 패턴에서 질적으로 다른 가정.
var 패턴은 일치되는 값의 이름을 지정하는 데만 사용되며, 중첩된 패턴(예: PetOwner{ Pet: var pet })에 있는 경우가 많습니다. 여기서 유용한 의미 체계는 속성이 쓸모없 object? 는 pet 형식으로 역참조되는 대신 Value 공용 구조체 형식Pet을 유지하는 것입니다.
들어오는 값이 클래스 형식 null 이면 공용 구조체 값 자체 null 인지 또는 포함된 값이 다음과 같은지에 관계없이 패턴이 null성공합니다.
if (result is null) { ... } // if (result == null || result.Value == null)
다른 공용 구조체 일치 패턴은 공용 구조체 값 자체가 아닌 null경우에만 성공합니다.
if (result is 1) { ... } // if (result != null && result.Value is 1)
마찬가지로 들어오는 값이 nullable 값 형식(구조체 공용 구조체 형식 래핑) null 인 경우 들어오는 값 자체가 다음 값인지에 관계없이 패턴이 nullnull성공합니다.
if (result is null) { ... } // if (result.HasValue == false || result.GetValueOrDefault().Value == null)
들어오는 값 자체가 아닌 null경우에만 다른 공용 구조체 일치 패턴이 성공합니다.
if (result is 1) { ... } // if (result.HasValue && result.GetValueOrDefault().Value is 1)
컴파일러는 boxing이 아닌 액세스 패턴에 의해 규정된 멤버를 통해 패턴 동작을 구현하는 것을 선호합니다. 올바른 형식 규칙의 범위 내에서 모든 최적화를 자유롭게 수행할 수 있지만 적용되도록 보장되는 최소 집합은 다음과 같습니다.
- 특정 형식
T을 확인하는 것을 의미하는 패턴의 경우, 메서드를 사용할 수 있고 ID 또는 암시적 참조/boxing 변환TS이 있는 경우TryGetValue(S value)해당 메서드를 사용하여 값을 가져옵니다. 그런 다음 패턴이 해당 값에 적용됩니다. 이러한 메서드가 두 개 이상 있는 경우 변환이 boxing 변환TS이 아닌 경우 사용할 수 있는 것이 좋습니다. 여전히 두 개 이상의 메서드가 있는 경우 구현 정의 방식으로 메서드가 선택됩니다. - 그렇지 않으면 속성을 사용할 수 있는 경우 확인을 의미하는
null패턴의 경우HasValue해당 속성을 사용하여 공용 구조체 값이 null인지 확인합니다. - 그렇지 않으면 패턴이 들어오는 공용 구조체의 속성에 액세스한
IUnion.Value결과에 적용됩니다.
공용 구조체 형식에 적용된 is-type 연산자는 공용 구조체 형식에 적용되는 형식 패턴과 동일한 의미를 가집니다.
통합 완전성
공용 구조체 형식은 대/소문자 형식에 의해 "소진"된 것으로 간주됩니다. 즉, switch 모든 공용 구조체의 대/소문자 형식을 처리하는 경우 식이 완전합니다.
var name = pet switch
{
Dog dog => ...,
Cat cat => ...,
// No warning about non-exhaustive switch
};
null 가능성
공용 구조체 속성의 Value null 상태는 다음과 같이 수정된 다른 속성처럼 추적됩니다.
- 공용 구조체 만들기 멤버가 호출되면(명시적으로 또는 공용 구조체 변환을 통해) 새 공용 구조체는 들어오는 값의
Valuenull 상태를 가져옵니다. - 비복싱 액세스 패턴
HasValue또는TryGetValue(...)공용 구조체 형식의 콘텐츠를 쿼리하는 데 사용되는 경우(명시적으로 또는 패턴 일치를 통해) 직접 검사된 것과Value같은 방식으로 Null 허용 여부 상태에 영향을Value줍니다. null 상태는Value분기에서true"null이 아님"이 됩니다.
공용 구조체 스위치가 완전하지 않은 경우에도 들어오는 공용 구조체 속성의 Value null 상태가 "어쩌면 null"인 경우 처리되지 않은 null에 경고가 표시됩니다.
Pet pet = GetNullableDog(); // 'pet.Value' is "maybe null"
var value = pet switch
{
Dog dog => ...,
Cat cat => ...,
// Warning: 'null' not handled
}
공용 구조체 인터페이스
다음 인터페이스는 공용 구조체 기능 구현에서 언어에서 사용됩니다.
공용 구조체 액세스 인터페이스
인터페이스는 IUnion 컴파일 시간에 형식을 공용 구조체 형식으로 표시하고 런타임에 공용 구조체 콘텐츠에 액세스하는 방법을 제공합니다.
public interface IUnion
{
// The value of the union or null
object? Value { get; }
}
컴파일러에서 생성된 공용 구조체는 이 인터페이스를 구현합니다.
예제 사용:
if (value is IUnion { Value: null }) { ... }
공용 구조체 선언
공용 구조체 선언은 C#에서 공용 구조체 형식을 선언하는 간결하고 의견 있는 방법입니다. 단일 개체 참조를 사용하여 해당 개체를 저장하는 구조체를 선언합니다. Value즉, 다음을 의미합니다.
- Boxing: 사례 형식 중에서 모든 값 형식은 항목에 boxed됩니다.
- 압축: 공용 구조체 값은 단일 필드만 포함합니다.
공용 구조체 선언은 대부분의 사용 사례를 아주 잘 다루기 위한 것입니다. 공용 구조체 선언을 사용하는 대신 특정 공용 구조체 형식을 직접 코딩하는 두 가지 주된 이유는 다음과 같습니다.
- 공용 구조체 동작을 얻기 위해 기존 형식을 공용 구조체 패턴에 맞게 조정합니다.
- 효율성 또는 interop 이유와 같은 다른 스토리지 전략을 구현합니다.
문법
공용 구조체 선언에는 이름과 공용 구조체 생성자 형식 목록이 있습니다.
union_declaration
: attributes? struct_modifier* 'partial'? 'union' identifier type_parameter_list?
'(' type (',' type)* ')' struct_interfaces? type_parameter_constraints_clause*
(`{` struct_member_declaration* `}` | ';')
;
구조체 멤버에 대한 제한 사항(§16.3) 외에도 다음이 공용 구조체 구성원에게 적용됩니다.
- 인스턴스 필드, 자동 속성 또는 필드와 유사한 이벤트는 허용되지 않습니다.
- 단일 매개 변수를 사용하여 명시적으로 선언된 공용 생성자는 허용되지 않습니다.
- 명시적으로 선언된 생성자는 생성된 생성자 중 하나에 직접 또는 간접적으로 대리자에 이니셜라이저를 사용해야
this(...)합니다.
공용 구조체 생성자 형식은 인터페이스, 형식 매개 변수, nullable 형식 및 기타 공용 구조체와 같이 변환object되는 모든 형식일 수 있습니다. 결과 사례가 겹치고 공용 구조체가 중첩되거나 null이 되는 것은 괜찮습니다.
예시들:
// Union of existing types
public union Pet(Cat, Dog, Bird);
// Union with function member
public union OneOrMore<T>(T, IEnumerable<T>)
{
public IEnumerable<T> AsEnumerable() => Value switch
{
IEnumerable<T> list => list,
T value => [value],
}
}
// "Discriminated" union with freshly declared case types
public record class None();
public record class Some<T>(T value);
public union Option<T>(None, Some<T>);
#### Lowering
A union declaration is lowered to a struct declaration with
* the same attributes, modifiers, name, type parameters and constraints,
* implicit implementations of `IUnion`,
* a `public object? Value { get; }` auto-property,
* a public constructor for each *union constructor* type,
* any members in the union declaration's body.
It is an error for user-declared members to conflict with generated members.
Example:
``` c#
public union Pet(Cat, Dog){ ... }
다음으로 낮춥다.
[Union] public struct Pet : IUnion
{
public Pet(Cat value) => Value = value;
public Pet(Dog value) => Value = value;
public object? Value { get; }
... // original body
}
질문 열기
[해결됨] 공용 구조체 선언이 레코드인가요?
공용 구조체 선언이 레코드 구조체로 낮아집니다.
이 기본 동작은 불필요하며 구성할 수 없다는 점을 감안할 때 사용 시나리오를 크게 제한합니다. 레코드는 사용되지 않거나 특정 요구 사항과 일치하지 않는 많은 코드를 생성합니다. 예를 들어 레코드는 해당 코드 bloat 때문에 컴파일러의 코드 베이스에서 거의 금지되어 있습니다. 기본값을 변경하는 것이 더 낫다고 생각합니다.
- 기본적으로 공용 구조체 선언은 공용 구조체별 멤버만 있는 일반 구조체를 선언합니다.
- 사용자는 레코드 공용 구조체를 선언할 수 있습니다.
record union U(E1, ...) ...
해상도: 공용 구조체 선언은 레코드 구조체가 아닌 일반 구조체입니다. 지원 record union ... 되지 않음
[해결됨] 공용 구조체 선언 구문
제안된 구문이 불완전하거나 불필요하게 제한되는 것처럼 보입니다. 예를 들어 기본 절이 허용되지 않는 것처럼 보입니다. 그러나 예를 들어 인터페이스를 구현해야 한다고 쉽게 상상할 수 있습니다.
element-types-list 외에도 키워드가 키워드로 대체되는 struct 일반record structstruct/선언과 union 구문이 일치해야 한다고 생각합니다.
해상도: 제한이 제거됩니다.
[해결됨] 공용 구조체 선언 멤버
인스턴스 필드, 자동 속성 또는 필드와 유사한 이벤트는 허용되지 않습니다.
이것은 임의적이고 절대적으로 불필요한 느낌.
해상도: 제한은 유지됩니다.
[해결됨] Nullable 값 형식을 Union 대/소문자 형식으로
공용 구조체의 사례 형식은 이러한 생성자의 매개 변수 형식 집합으로 식별됩니다. 공용 구조체의 사례 형식은 이러한 팩터리 메서드의 매개 변수 형식 집합으로 식별됩니다.
동시에 다음을 수행합니다.
TryGetValue각 사례 형식에 대한 메서드입니다. 메서드는 다음과 같은 방법으로 지정된 사례 형식에 해당하는 형식의 단일 out 매개 변수를 반환bool하고 사용합니다.
- 사례 형식이 nullable 값 형식인 경우 매개 변수의 형식은 ID를 기본 형식으로 변환할 수 있어야 합니다.
- 그렇지 않으면 형식이 대/소문자 형식으로 ID 변환 가능해야 합니다.
특히 형식 패턴에서 nullable 값 형식을 대상 형식으로 사용할 수 없는 경우 사례 형식에 nullable 값 형식이 있다는 장점이 있나요? 생성자의/팩터리 매개 변수 형식이 nullable 값 형식인 경우 해당 사례 형식이 기본 형식이라고 말할 수 있습니다. 그런 다음 메서드에 대한 추가 절이 TryGetValue 필요하지 않으며 모든 out 매개 변수는 대/소문자 형식입니다.
해상도: 제안이 승인됨
[해결됨] 속성의 Value 기본 null 허용 상태
null을 허용하지 않는 공용 구조체 형식의 경우 기본 상태는
Value"어쩌면 null"이 아닌 "null이 아닙니다"입니다.
속성이 일부 일반 인터페이스에서 정의되지 않지만 선언된 형식에 특별히 속하는 API인 새 디자인을 Value 사용하면 위에서 인용한 규칙이 지나치게 엔지니어링된 것처럼 느껴집니다. 또한 이 규칙은 소비자가 nullable 형식을 사용하지 않는 경우 nullable 형식을 사용하도록 강제할 가능성이 높습니다.
예를 들어 다음 공용 구조체 선언을 고려합니다.
union U1(int, bool, DateTime);
따옴표 붙은 규칙에 따라 기본 상태는 Value "not null"입니다. 그러나 형식의 동작과 default(U1).Valuenull일치하지 않습니다. 동작을 다시 정렬하기 위해 소비자는 하나 이상의 사례 형식을 null 허용으로 만들어야 합니다. 다음과 같습니다.
union U1(int?, bool, DateTime);
그러나 이는 바람직하지 않을 수 있으며, 소비자는 값을 사용하여 명시적 생성 int? 을 허용하지 않을 수 있습니다.
제안: 따옴표 붙은 규칙을 제거합니다. null 허용 분석은 속성의 Value 주석을 사용하여 기본 null 허용을 유추해야 합니다.
해상도: 제안이 승인됨
[해결됨] 공용 구조체 값 형식의 Nullable에 대한 공용 구조체 일치
패턴의 들어오는 값이 공용 구조체 형식인 경우 패턴에 따라 공용 구조체 값의 내용이 "래핑 해제"될 수 있습니다.
패턴 Nullable<union type>의 들어오는 값이 ??인 경우 이 규칙을 시나리오로 확장해야 하나요?
다음 시나리오를 고려하세요.
static bool Test1(StructUnion? u)
{
return u is 1;
}
static bool Test2(ClassUnion? u)
{
return u is 1;
}
Test1 및 Test2의 u is 1 의미는 매우 다릅니다. Test1에서는 공용 구조체 일치가 아니며 Test2에서는 일치합니다.
아마도 패턴 일치는 일반적으로 다른 상황에서와 같이 "공용 구조체 일치"를 통해 Nullable<T> "파고"해야합니다.
우리가 그것으로 가면, 다음 에 대한 Nullable<union type> 조합 일치 null 패턴은 클래스에 대해 작동해야합니다.
즉, 패턴이 true인 경우 (!nullableValue.HasValue || nullableValue.Value.Value is null)입니다.
해상도: 제안이 승인되었습니다.
"잘못된" API에 대해 어떻게 해야 할까요?
컴파일러는 일치 항목처럼 보이지만 그렇지 않으면 "나쁜" 공용 구조체 일치 API에 대해 무엇을 해야 하나요? 예를 들어 컴파일러는 일치하는 서명이 있는 TryGetValue/HasValue를 찾지만 필요한 사용자 지정 한정자 또는 알 수 없는 기능이 필요하기 때문에 "잘못되었습니다". 컴파일러가 API를 자동으로 무시하거나 오류를 보고해야 하나요? 마찬가지로 API는 사용되지 않음/실험적으로 표시될 수 있습니다. 컴파일러가 진단을 보고하거나, API를 자동으로 사용하거나, API를 자동으로 사용하지 않아야 하나요?
공용 구조체 선언에 대한 형식이 누락된 경우
또는 누락된 경우 UnionAttributeIUnionIUnion<TUnion> 어떻게 되나요? 오류? 합성? 뭔가 다른?
[해결됨] 제네릭 IUnion 인터페이스 디자인
형식 매개 변수IUnion<TUnion>를 IUnion 상속하거나 제약해서는 안 되는 IUnion<TUnion> 인수가 만들어졌습니다. 우리는 다시 방문해야합니다.
해상도:IUnion<TUnion> 지금은 인터페이스가 제거됩니다.
[해결됨] 대/소문자 형식으로 Nullable 값 형식 및 해당 형식과의 상호 작용 TryGetValue
위의 규칙은 사례 형식이 nullable 값 형식인 경우 해당 TryGetValue 메서드에 사용되는 매개 변수 형식이 기본 형식이어야 한다고 명시 합니다 .
이는 값이 이 메서드를 null 통해 생성되지 않을 것이라는 사실에 의해 동기를 부여합니다. 소비 쪽에서는 nullable 값 형식이 형식 패턴으로 허용되지 않는 반면 기본 형식에 대한 일치 항목은 이 메서드의 호출에 매핑할 수 있어야 합니다.
이 래핑 해제에 동의하는지 확인해야 합니다.
해상도: 동의/확인됨
비복싱 공용 구조체 액세스 패턴
적합한 HasValue API를 TryGetValue 찾기 위한 정확한 규칙을 지정해야 합니다.
상속이 관련되어 있나요? 읽기/쓰기 HasValue 가 허용 가능한 일치 항목인가요? 등.
[해결됨] TryGetValue 일치하는 변환
공용 구조체 일치 섹션은 다음과 같이 말합니다.
특정 형식
T을 확인하는 것을 의미하는 패턴의 경우 메서드를 사용할 수 있고 암시적 변환TS이 있는 경우TryGetValue(S value)해당 메서드를 사용하여 값을 가져옵니다.
암시적 변환 집합이 어떤 방식으로든 제한되는가? 예를 들어 사용자 정의 변환이 허용되었나요? 튜플 변환 및 기타 사소한 변환은 어떻습니까? 그 중 일부는 심지어 표준 변환입니다.
메서드 집합 TryGetValue 이 다른 방법으로 제한되는가요? 예를 들어 Union Patterns 섹션은 대/소문자 형식과 일치하는 매개 변수 형식의 메서드만 고려됨을 의미합니다.
public bool TryGetValue(out T value)각 사례 유형T에 대한 메서드입니다.
명시적 대답을 하는 것이 좋습니다.
해상도: 암시적 ID 또는 참조 또는 boxing 변환만 고려됩니다.
TryGetValue 및 nullable 분석
비복싱 액세스 패턴
HasValue또는TryGetValue(...)공용 구조체 형식의 콘텐츠를 쿼리하는 데 사용되는 경우(명시적으로 또는 패턴 일치를 통해) 직접 검사된 것과Value같은 방식으로 Null 허용 여부 상태에 영향을Value줍니다. null 상태는Value분기에서true"null이 아님"이 됩니다.
메서드 집합 TryGetValue 이 어떤 방식으로 제한되는가? 예를 들어 Union Patterns 섹션은 대/소문자 형식과 일치하는 매개 변수 형식의 메서드만 고려됨을 의미합니다.
public bool TryGetValue(out T value)각 사례 유형T에 대한 메서드입니다.
명시적 대답을 하는 것이 좋습니다.
구조체 공용 구조체 형식의 값에 default 대한 규칙 명확히
참고: 아래에 언급된 기본 Null 허용 여부 규칙이 제거되었습니다.
참고: 아래에 언급된 "기본" 올바른 형식 규칙이 제거되었습니다. 우리는 이것이 우리가 원하는 것을 확인해야합니다.
Null 허용 여부 섹션은 다음을 말합니다.
null을 허용하지 않는 공용 구조체 형식의 경우 기본 상태는
Value"어쩌면 null"이 아닌 "null이 아닙니다"입니다.
아래 예제에서는 현재 구현이 "null이 아님"으로 간주합니다 Values2 .
S2 s2 = default;
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => throw null!;
public S2(bool x) => throw null!;
object? System.Runtime.CompilerServices.IUnion.Value => throw null!;
}
동시에 , 잘 형성 섹션은 말한다 :
- 기본값: 공용 구조체 형식이 값 형식인 경우 기본값은
null해당Value형식입니다.- 기본 생성자: 공용 구조체 형식에 nullary(인수 없음) 생성자가 있는 경우 결과 공용 구조체는
null해당Value형식입니다.
이와 같은 구현은 위의 예제에 대한 nullable 분석 동작과 모순됩니다.
올바른 형식의 규칙을 조정해야 하나요, 아니면 상태가 Valuedefault "어쩌면 null"이어야 하나요?
후자의 경우 초기화 S2 s2 = default; 에서 Null 허용 여부 경고를 생성해야 하나요?
형식 매개 변수가 하나의 형식으로 제한되는 경우에도 공용 구조체 형식이 되지 않는지 확인합니다.
class C1 : System.Runtime.CompilerServices.IUnion
{
private readonly object _value;
public C1(int x) { _value = x; }
public C1(string x) { _value = x; }
object System.Runtime.CompilerServices.IUnion.Value => _value;
}
class Program
{
static bool Test1<T>(T u) where T : C1
{
return u is int; // Not a union matching
}
static bool Test2<T>(T u) where T : C1
{
return u is string; // Not a union matching
}
}
사후 조건 특성은 Union 인스턴스의 기본 null 허용률에 영향을 주나요?
참고: 아래에 언급된 기본 Null 허용 여부 규칙이 제거되었습니다. 또한 더 이상 공용 구조체 생성 메서드에서 속성의 Value 기본 null 허용 가능성을 유추하지 않습니다. 따라서 문제는 더 이상 현재 디자인에 더 이상 적용되지 않습니다.
null을 허용하지 않는 공용 구조체 형식의 경우 기본 상태는
Value"어쩌면 null"이 아닌 "null이 아닙니다"입니다.
다음 시나리오에서 예상되는 경고인가요?
#nullable enable
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null!;
public S1([System.Diagnostics.CodeAnalysis.NotNull] bool? x) => throw null!;
object? System.Runtime.CompilerServices.IUnion.Value => throw null!;
}
class Program
{
static void Test2(S1 s)
{
// warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive).
// For example, the pattern 'null' is not covered.
_ = s switch { int => 1, bool => 3 }; //
}
}
공용 구조체 변환
[해결됨] 그들은 우선 순위에 따라 다른 변환 중 어디에 속합니까?
공용 구조체 변환은 다른 형태의 사용자 정의 변환처럼 느껴집니다. 따라서 현재 구현은 암시적 사용자 정의 변환을 분류하는 데 실패한 직후에 분류되며, 존재할 경우 사용자 정의 변환의 또 다른 형식으로 처리됩니다. 여기에는 다음과 같은 결과가 있습니다.
- 암시적 사용자 정의 변환은 공용 구조체 변환보다 우선합니다.
- 코드에서 명시적 캐스트를 사용하는 경우 명시적 사용자 정의 변환이 공용 구조체 변환보다 우선합니다.
- 코드에 명시적 캐스트가 없는 경우 공용 구조체 변환은 명시적 사용자 정의 변환보다 우선합니다.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => ...
public S1(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static implicit operator S1(int x) => ...
}
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => ...
public S2(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static explicit operator S2(int x) => ...
}
class Program
{
static S1 Test1() => 10; // implicit operator S1(int x) is used
static S1 Test2() => (S1)20; // implicit operator S1(int x) is used
static S2 Test3() => 10; // Union conversion S2.S2(int) is used
static S2 Test4() => (S2)20; // explicit operator S2(int x)
}
이것이 우리가 좋아하는 동작인지 확인해야 합니다. 그렇지 않으면 변환 규칙을 명확히 해야 합니다.
해결 방법:
작업 그룹에 의해 승인되었습니다.
[해결됨] 생성자 매개 변수의 Ref-ness
현재 언어는 사용자 정의 변환 연산자에서 값 및 매개 변수만 허용합니다 in .
이러한 제한 사항이 공용 구조체 변환에 적합한 생성자에도 적용되는 이유인 것 같습니다.
제안:
위의 in 섹션에 대한 case type constructor 정의를 조정합니다.Union types
-For each public constructor with exactly one parameter, the type of that parameter is considered a *case type* of the union type.
+For each public constructor with exactly one **by-value or `in`** parameter, the type of that parameter is considered a *case type* of the union type.
해결 방법:
현재 작업 그룹에서 승인합니다. 그러나 사례 형식 생성자 집합과 공용 구조체 형식 변환에 적합한 생성자 집합을 "분할"하는 것이 좋습니다.
[해결됨] Nullable 변환
Nullable 변환 섹션에는 기본으로 사용할 수 있는 변환이 명시적으로 나열됩니다. 현재 사양은 해당 목록에 대한 조정을 제안하지 않습니다. 이로 인해 다음 시나리오에 오류가 발생합니다.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1? Test1(int x)
{
return x; // error CS0029: Cannot implicitly convert type 'int' to 'S1?'
}
}
제안:
공용 구조체 변환을 통해 지원되는 암시적 nullable 변환 S 을 지원하도록 T? 사양을 조정합니다.
특히 공용 구조체 형식이 형식으로의 통합 변환이 있고 대/소문자 형식 T? 인 경우 형식 또는 식 E 에서 E 형식 CC 으로의 암시적 변환이 T있다고 가정 T 합니다.
nullable이 아닌 값 형식이어야 E 하는 형식에 대한 요구 사항은 없습니다.
변환은 기본 공용 구조체 변환에서 ST 다음으로의 래핑 T 으로 평가됩니다. T?
해결 방법:
승인.
[해결됨] 해제된 변환
해제된 공용 구조체 변환을 지원하도록 해제된 변환 섹션을 조정하시겠습니까? 현재는 허용되지 않습니다.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(int? x)
{
return x; // error CS0029: Cannot implicitly convert type 'int?' to 'S1'
}
static S1? Test2(int? y)
{
return y; // error CS0029: Cannot implicitly convert type 'int?' to 'S1?'
}
}
해결 방법:
지금은 해제 된 노조 변환이 없습니다. 토론의 일부 참고 사항:
사용자 정의 변환에 대한 비유는 여기서 약간 구분됩니다. 일반적으로 공용 구조체는 들어오는 null 값을 포함할 수 있습니다. 리프팅에서 값이 저장된 공용 구조체 형식
null의 인스턴스를 만들어야 하는지 또는 값을Nullable<Union>만들어야null하는지 여부는 명확하지 않습니다.
[해결됨] 기본 형식의 인스턴스에서 공용 구조체 변환을 차단하시겠습니까?
현재 동작이 혼동될 수 있습니다.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(System.ValueType x)
{
}
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(System.ValueType x)
{
return x; // Union conversion
}
static S1 Test2(System.ValueType y)
{
return (S1)y; // Unboxing conversion
}
}
언어는 기본 형식에서 사용자 정의 변환을 선언하는 것을 명시적으로 허용하지 않습니다. 따라서 이러한 공용 구조체 변환을 허용하지 않을 수 있습니다.
해결 방법:
지금은 특별한 아무것도하지 않습니다. 제네릭 시나리오는 어쨌든 완전히 보호할 수 없습니다.
[해결됨] 인터페이스 형식의 인스턴스에서 공용 구조체 변환을 차단하시겠습니까?
현재 동작이 혼동될 수 있습니다.
struct S1 : I1, System.Runtime.CompilerServices.IUnion
{
public S1(I1 x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
interface I1 { }
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(I1 x) => throw null;
public S2(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class C3 : System.Runtime.CompilerServices.IUnion
{
public C3(I1 x) => throw null;
public C3(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(I1 x)
{
return x; // Union conversion
}
static S1 Test2(I1 x)
{
return (S1)x; // Unboxing
}
static S2 Test3(I1 x)
{
return x; // Union conversion
}
static S2 Test4(I1 x)
{
return (S2)x; // Union conversion
}
static C3 Test3(I1 x)
{
return x; // Union conversion
}
static C3 Test4(I1 x)
{
return (C3)x; // Reference conversion
}
}
언어는 기본 형식에서 사용자 정의 변환을 선언하는 것을 명시적으로 허용하지 않습니다. 따라서 이러한 공용 구조체 변환을 허용하지 않을 수 있습니다.
해결 방법:
지금은 특별한 아무것도하지 않습니다. 제네릭 시나리오는 어쨌든 완전히 보호할 수 없습니다.
IUnion 인터페이스의 네임스페이스
인터페이스에 대한 네임스페이스를 IUnion 포함하는 것은 지정되지 않은 상태로 유지됩니다. 의도를 네임스페이스에 유지 global 하려는 경우 명시적으로 명시해 보겠습니다.
제안: 단순히 간과된 것이라면 네임스페이스를 사용할 System.Runtime.CompilerServices 수 있습니다.
형식으로서의 Union 클래스
[해결됨] 인스턴스 자체에 대한 확인 null
공용 구조체 형식이 클래스 형식인 경우 값 자체가 null일 수 있습니다. 그렇다면 null 검사는 어떻습니까?
패턴이 null 속성을 확인하기 Value 위해 공동으로 선택되었으므로 공용 구조체 자체가 null이 아닌지 어떻게 확인합니까?
다음은 그 예입니다.
- 구조체인
Union경우S값은 그 자체가 있을 때만s해당됩니다trueS?null.s is null클래스c is null인Union경우C값C?은false그 자체가null될 때c이지만truec그 자체가 아닌nullc.Value경우입니다null.
다음은 또 다른 예제입니다.
class C1 : IUnion
{
private readonly object? _value;
public C1(){}
public C1(int x) { _value = x; }
public C1(string x) { _value = x; }
object? IUnion.Value => _value;
}
class Program
{
static int Test1(C1? u)
{
// warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive).
// For example, the pattern 'null' is not covered.
// This is very confusing, the switch expression is indeed not exhaustive (u itself is not
// checked for null), but there is a case 'null => 3' in the switch expression.
// It looks like the only way to shut off the warning is to use 'case _'. Adding it removes
// all benefits of exhaustiveness checking, any union case could be missing and there would
// be no diagnostic about that.
return u switch { int => 1, string => 2, null => 3 };
}
}
디자인의 이 부분은 공용 구조체 형식이 구조체라는 기대를 중심으로 명확하게 최적화되어 있습니다. 몇 가지 옵션:
- 참 안됐군요. 패턴 일치 대신 null 검사에 사용합니다
==. -
null패턴(및 다른 패턴의 암시적 null 체크)을 공용 구조체 값과 해당Value속성u is null ==> u == null || u.Value == null모두에 적용합니다. - 클래스가 공용 구조체 형식이 되는 것을 허용하지 않습니다!
[해결됨] 클래스에서 Union 파생
클래스가 클래스를 Union기본 클래스로 사용하는 경우 현재 사양에 따라 클래스 자체가 됩니다 Union. 이는 인터페이스의 IUnion 구현을 자동으로 "상속"하기 때문에 다시 구현할 필요가 없기 때문에 발생합니다. 동시에 파생 형식의 생성자는 이 새 Union형식의 형식 집합을 정의합니다. 두 클래스를 중심으로 매우 이상한 언어 동작을 쉽게 얻을 수 있습니다.
class C1 : IUnion
{
private readonly object _value;
public C1(long x) { _value = x; }
public C1(string x) { _value = x; }
object IUnion.Value => _value;
}
class C2(int x) : C1(x);
class Program
{
static int Test1(C1 u)
{
// Good
return u switch { long => 1, string => 2, null => 3 };
}
static int Test2(C2 u)
{
// error CS8121: An expression of type 'C2' cannot be handled by a pattern of type 'long'.
// error CS8121: An expression of type 'C2' cannot be handled by a pattern of type 'string'.
return u switch { long => 1, string => 2, null => 3 };
}
}
몇 가지 옵션:
클래스 형식이 형식인 경우 변경합니다
Union. 예를 들어 클래스는 모두 true인Union경우 형식입니다.- 파생 형식은
sealed형식으로Union간주되지 않으므로 혼동을 일으키기 때문입니다. - 해당 기지 중 어느 것도 구현하지 않습니다.
IUnion
이것은 여전히 완벽하지 않습니다. 규칙은 너무 미묘합니다. 실수를 하기 쉽습니다. 선언에 대한 진단은 없지만
Union일치는 작동하지 않습니다.- 파생 형식은
클래스가 공용 구조체 형식이 되는 것을 허용하지 않습니다.
[해결됨] is-type 연산자
is-type 연산 자는 런타임 형식 검사로 지정됩니다. 구문적으로 형식 패턴처럼 보이지만 그렇지 않습니다. 따라서 특수 Union일치는 사용되지 않으므로 사용자 혼란이 발생할 수 있습니다.
struct S1 : IUnion
{
private readonly object _value;
public S1(int x) { _value = x; }
public S1(string x) { _value = x; }
object IUnion.Value => _value;
}
class Program
{
static bool Test1(S1 u)
{
return u is int; // warning CS0184: The given expression is never of the provided ('int') type
}
static bool Test2(S1 u)
{
return u is string and ['1', .., '2']; // Good
}
}
재귀 공용 구조체의 경우 형식 패턴은 경고를 표시하지 않을 수 있지만 사용자가 생각하는 작업을 수행하지는 않습니다.
해상도: 형식 패턴으로 작동해야 합니다.
목록 패턴
목록 패턴은 항상 일치와 함께 Union 실패합니다.
struct S1 : IUnion
{
private readonly object _value;
public S1(int[] x) { _value = x; }
public S1(string[] x) { _value = x; }
object IUnion.Value => _value;
}
class Program
{
static bool Test1(S1 u)
{
// error CS8985: List patterns may not be used for a value of type 'object'. No suitable 'Length' or 'Count' property was found.
// error CS0021: Cannot apply indexing with [] to an expression of type 'object'
return u is [10];
}
}
static class Extensions
{
extension(object o)
{
public int Length => 0;
}
}
기타 질문
- 공용 구조체 변환에서 생성자의 사용과 공용 구조체 패턴 일치의
TryGetValue(...)사용은 모두 여러 생성자가 적용될 때 관대하도록 지정됩니다. 이것은 잘 형성 된 규칙에 따라 중요하지 않아야하지만, 우리는 그것에 편안합니까? - 이 사양은 공용 구조체 형식 자체에 있는 속성이 아닌
Value속성의IUnion.Value구현을 미묘하게 사용합니다. 이는 패턴을 구현하기 위해 기존 형식(다른 용도에 대한 자체Value속성이 있을 수 있음)에 더 큰 유연성을 제공하기 위한 것입니다. 그러나 그것은 어색하고 다른 회원들이 노조 유형에서 직접 발견되고 사용되는 방법과 일치하지 않습니다. 변경해야 하나요? 몇 가지 다른 옵션:- 공용
Value속성을 노출하려면 공용 구조체 형식이 필요합니다. - 공용
Value속성이 있는 경우 선호하지만 그렇지 않은 경우 구현으로IUnion.Value대체합니다(규칙과GetEnumerator유사).
- 공용
- 제안된 공용 구조체 선언 구문은 특히 사례 유형을 표현할 때 보편적으로 사랑되지 않습니다. 지금까지의 대안도 비판에 부합하지만, 결국 변화를 하게 될 수도 있습니다. 현재 문제에 대한 몇 가지 주요 관심사는 다음과 같습니다.
- 대/소문자 형식 간의 구분 기호로 쉼표는 순서가 중요하다는 것을 암시할 수 있습니다.
- 괄호로 읽은 목록은 매개 변수 이름이 없더라도 기본 생성자와 너무 비슷합니다.
- 중괄호 안에 "cases"가 있는 열거형과 너무 다릅니다.
- 공용 구조체 선언은 단일 참조 필드가 있는 구조체를 생성하지만 동시 컨텍스트에서 사용할 때 예기치 않은 동작에 다소 취약합니다. 예를 들어 사용자 정의 함수 멤버가 두 번 이상 역참조
this하는 경우 포함하는 변수는 두 액세스 사이의 다른 스레드에 의해 전체적으로 다시 할당되었을 수 있습니다. 컴파일러는 필요한 경우 로컬로 복사this할 코드를 생성할 수 있습니다. 그것은 해야 하는가? 일반적으로 어떤 수준의 동시성 복원력이 바람직하고 합리적으로 달성할 수 있습니까?
C# feature specifications