공용 구조체 형식은 여러 사례 형식 중 하나일 수 있는 값을 나타냅니다. 공용 구조체는 각 사례 유형에서 암시적 변환, 철저한 패턴 일치 및 향상된 Null 허용 여부 추적을 제공합니다. 키워드를 union 사용하여 공용 구조체 형식을 선언합니다.
public union Pet(Cat, Dog, Bird);
이 선언은 세 가지 대/소문자 Pet 형식을 Cat사용하여 공용 구조체를 DogBird만듭니다. 모든 사례 형식 값을 변수에 Pet 할당할 수 있습니다. 컴파일러는 식이 switch 모든 사례 형식을 포함하도록 합니다.
C# 언어 참조는 가장 최근에 릴리스된 C# 언어 버전을 문서화합니다. 또한 예정된 언어 릴리스의 공개 미리 보기 기능에 대한 초기 설명서도 포함되어 있습니다.
설명서는 언어의 마지막 세 버전 또는 현재 공개 미리 보기에서 처음 도입된 기능을 식별합니다.
팁 (조언)
C#에서 기능이 처음 도입된 시기를 찾으려면 C# 언어 버전 기록에 대한 문서를 참조하세요.
값이 고정된 형식 집합 중 하나여야 하고 컴파일러가 모든 가능성을 처리하도록 적용하려는 경우 공용 구조체를 선언합니다. 일반적인 시나리오는 다음과 같습니다.
-
결과 또는 오류 반환: 메서드는 성공 값 또는 오류 값을 반환하고 호출자는 둘 다 처리해야 합니다. 같은
union Result(Success, Error)공용 구조체는 결과 집합을 명시적으로 만듭니다. -
메시지 또는 명령 디스패치: 시스템에서 닫힌 메시지 유형 집합을 처리합니다. 공용 구조체는 새 메시지 형식이 아직 처리하지 않는 모든
switch시간에 컴파일 시간 경고를 생성하도록 합니다. - 표식 인터페이스 또는 추상 기본 클래스 바꾸기: 인터페이스 또는 추상 클래스를 사용하여 패턴 일치를 위한 형식만 그룹화하면 상속 또는 공유 멤버 없이도 완전한 검사를 수행할 수 있습니다.
공용 구조체는 중요한 방법으로 다른 형식 선언과 다릅니다.
-
class또는struct공용 구조체와 달리 공용 구조체는 새 데이터 멤버를 정의하지 않습니다. 대신 기존 형식을 닫은 대체 집합으로 구성합니다. - 공용
interface구조체와 달리 공용 구조체는 닫혀 있습니다. 선언에서 전체 사례 형식 목록을 정의하고 컴파일러는 전체 검사에 해당 목록을 사용합니다. - 공용 구조체와
record달리 공용 구조체는 같음, 복제 또는 분해 동작을 추가하지 않습니다. 노조는 "어떤 필드가 있습니까?"보다는 "어떤 경우입니까?"에 초점을 맞춥니다.
중요합니다
.NET 11 미리 보기 2에서는 런타임에 인터페이스와 IUnion 인터페이스가 UnionAttribute 포함되지 않습니다. 공용 구조체 형식을 사용하려면 직접 선언해야 합니다. 필요한 선언을 보려면 Union 구현을 참조하세요.
공용 구조체 선언
공용 구조체 선언은 이름 및 사례 유형 목록을 지정합니다.
public union Pet(Cat, Dog, Bird);
대/소문 자 object형식은 클래스, 구조체, 인터페이스, 형식 매개 변수, nullable 형식 및 기타 공용 구조체를 포함하여 변환되는 모든 형식일 수 있습니다. 다음 예제에서는 다양한 사례 유형 가능성을 보여 줍니다.
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public record class None;
public record class Some<T>(T Value);
public union Option<T>(None, Some<T>);
public union IntOrString(int, string);
사례 형식이 값 형식(예: int)인 경우 공용 구조체의 Value 속성에 저장될 때 값이 boxed됩니다. 공용 구조체는 콘텐츠를 단일 object? 참조로 저장합니다.
공용 구조체 선언에는 구조체와 마찬가지로 일부 제한 사항이 적용된 추가 멤버가 있는 본문이 포함될 수 있습니다. 공용 구조체 선언에는 인스턴스 필드, 자동 속성 또는 필드와 유사한 이벤트를 포함할 수 없습니다. 컴파일러가 공용 생성자를 공용 생성 멤버로 생성하므로 단일 매개 변수를 사용하여 공용 생성자를 선언할 수도 없습니다.
public union OneOrMore<T>(T, IEnumerable<T>)
{
public IEnumerable<T> AsEnumerable() => Value switch
{
T single => [single],
IEnumerable<T> multiple => multiple,
_ => []
};
}
공용 구조체 변환
암시적 공용 구조체 변환 은 각 사례 형식에서 공용 구조체 형식으로 존재합니다. 생성자를 명시적으로 호출할 필요가 없습니다.
static void BasicConversion()
{
Pet pet = new Dog("Rex");
Console.WriteLine(pet.Value); // output: Dog { Name = Rex }
Pet pet2 = new Cat("Whiskers");
Console.WriteLine(pet2.Value); // output: Cat { Name = Whiskers }
}
공용 구조체 변환은 생성된 해당 생성자를 호출하여 작동합니다. 동일한 형식에 대해 사용자 정의 암시적 변환 연산자가 있는 경우 사용자 정의 연산자는 공용 구조체 변환보다 우선합니다. 변환 우선 순위에 대한 자세한 내용은 언어 사양을 참조하세요.
Null 허용 공용 구조체(T?)로의 공용 구조체 변환은 공용 구조체 형식인 경우에도 T 작동합니다.
static void NullableUnionExample()
{
Pet? maybePet = new Dog("Buddy");
Pet? noPet = null;
Console.WriteLine(Describe(maybePet)); // output: Dog: Buddy
Console.WriteLine(Describe(noPet)); // output: no pet
static string Describe(Pet? pet) => pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
}
공용 구조체 일치
공용 구조체 형식에서 패턴 일치를 사용하는 경우 패턴은 공용 구조체 값 자체가 아니라 공용 구조체의 Value 속성에 적용됩니다. 이 "래핑 해제" 동작은 공용 구조체가 패턴 일치에 투명하다는 것을 의미합니다.
static void PatternMatching()
{
Pet pet = new Dog("Rex");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
이 규칙 var 의 두 가지 패턴은 예외입니다. 패턴과 삭제 _ 패턴은 해당 속성이 아닌 공용 구조체 값 자체에 Value 적용됩니다. ()Nullable<Pet>를 반환할 때 GetPet() 공용 구조체 값을 캡처하는 데 Pet? 사용합니다var.
if (GetPet() is var pet) { /* pet is the Pet? value returned from GetPet */ }
논리 패턴에서 각 분기는 래핑 해제 규칙을 개별적으로 따릅니다. 다음 패턴은 null이 아니고Value null이 Pet? 아닌지 테스트합니다.
GetPet() switch
{
var pet and not null => ..., // 'var pet' captures the Pet?; 'not null' checks Value
}
메모
패턴이 적용되기 Value때문에 일반적으로와 같은 pet is Pet 패턴은 일치하지 Pet 않습니다. 이는 공용 구조체 자체가 아니라 공용 구조체의 내용 에 대해 테스트되기 때문입니다.
Null 일치
구조체 공용 구조체의 경우 패턴은 null null인지 여부를 Value 확인합니다.
static void NullHandling()
{
Pet pet = default;
Console.WriteLine(pet.Value is null); // output: True
var description = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
Console.WriteLine(description); // output: no pet
}
클래스 기반 공용 구조체의 null 경우 공용 구조체 참조 자체가 null이거나 해당 Value 속성이 null이면 성공합니다.
Result<string>? result = null;
if (result is null) { /* true — the reference is null */ }
Result<string> empty = new Result<string>((string?)null);
if (empty is null) { /* true — Value is null */ }
nullable 공용 구조체 형식(Pet?) null 의 경우 nullable 래퍼에 값이 없거나 기본 공용 구조체가 Value null일 때 성공합니다.
통합 완전성
식 switch 은 모든 사례 형식의 공용 구조체를 처리할 때 완전합니다. 컴파일러는 사례 형식이 처리되지 않은 경우에만 경고합니다. 모든 형식과 일치하도록 무시 패턴(_) 또는 var 패턴을 포함할 필요가 없습니다.
static void PatternMatching()
{
Pet pet = new Dog("Rex");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
공용 구조체 속성의 Value null 상태가 "null일 수 있음"인 경우 경고를 방지하기 위해 처리 null 해야 합니다.
static void NullHandling()
{
Pet pet = default;
Console.WriteLine(pet.Value is null); // output: True
var description = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
Console.WriteLine(description); // output: no pet
}
null 가능성
컴파일러는 다음 규칙을 통해 공용 구조체 속성의 Value null 상태를 추적합니다.
- 생성자 또는 공용 구조체 변환
Value을 통해 사례 형식에서 공용 구조체 값을 만들 때 들어오는 값의 null 상태를 가져옵니다. - 비복싱 액세스 패턴
HasValue또는TryGetValue(...)멤버가 공용 구조체의 콘텐츠를 쿼리할 때 null 상태는Value분기에서true"null이 아님"이 됩니다.
사용자 지정 공용 구조체 형식
컴파일러는 선언을 union 선언으로 struct 변환합니다. 구조체는 특성으로 [System.Runtime.CompilerServices.Union] 표시되고 인터페이스를 IUnion 구현합니다. 여기에는 속성과 함께 각 사례 형식에 대한 공용 생성자 및 암시적 변환이 Value 포함됩니다. 생성된 양식은 의견이 있습니다. 항상 구조체이고, 항상 값 형식 사례를 상자로 지정하고, 항상 콘텐츠를 .로 object?저장합니다.
클래스 기반 공용 구조체, 사용자 지정 스토리지 전략, interop 지원 등 다른 동작이 필요하거나 기존 형식을 조정하려는 경우 수동으로 공용 구조체 형식을 만들 수 있습니다.
특성이 있는 [Union] 모든 클래스 또는 구조체는 기본 공용 구조체 패턴을 따르는 경우 공용 구조체 형식입니다. 기본 공용 구조체 패턴에는 다음이 필요합니다.
-
[Union]형식의 특성입니다. - 하나 이상의 공용 생성자( 각각 값별 또는
in매개 변수가 하나씩 포함됨). 각 생성자의 매개 변수 형식은 사례 형식을 정의합니다. - 접근자가 있는 형식(또는
object)의 publicValue속성입니다get.object?
모든 조합원은 공용이어야 합니다. 컴파일러는 이러한 멤버를 사용하여 공용 구조체 변환, 패턴 일치 및 완전성 검사를 구현합니다. boxing이 아닌 액세스 패턴을 구현하거나 클래스 기반 공용 구조체 형식을 만들 수도 있습니다.
컴파일러는 사용자 지정 공용 구조체 형식이 다음 동작 규칙을 충족한다고 가정합니다.
-
소리:
Value항상 대/소문자 형식 중 하나의 값을 반환하거나 반환null하며 다른 형식의 값은 반환하지 않습니다. 구조체 공용 구조체의default경우 다음을Valuenull생성합니다. -
안정성: 사례 형식
Value에서 공용 구조체 값을 만드는 경우 해당 사례 형식(또는null입력이 있는null경우)과 일치합니다. - 생성 동등성: 값이 암시적으로 두 개의 서로 다른 대/소문자 형식으로 변환할 수 있는 경우 두 생성 멤버 모두 동일한 관찰 가능한 동작을 생성합니다.
-
액세스 패턴 일관성: 멤버와
TryGetValue멤버(HasValue있는 경우)는 직접 확인하는 것과 동일하게 동작합니다Value.
다음 예제에서는 사용자 지정 공용 구조체 형식을 보여줍니다.
[System.Runtime.CompilerServices.Union]
public struct Shape : System.Runtime.CompilerServices.IUnion
{
private readonly object? _value;
public Shape(Circle value) { _value = value; }
public Shape(Rectangle value) { _value = value; }
public object? Value => _value;
}
public record class Circle(double Radius);
public record class Rectangle(double Width, double Height);
static void ManualUnionExample()
{
Shape shape = new Shape(new Circle(5.0));
var area = shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
};
Console.WriteLine($"{area:F2}"); // output: 78.54
}
비복싱 액세스 패턴
사용자 지정 공용 구조체 형식은 선택적으로 비복싱 액세스 패턴을 구현하여 패턴 일치 중에 boxing 없이 값 형식 사례에 강력한 형식의 액세스를 사용하도록 설정할 수 있습니다. 이 패턴에는 다음이 필요합니다.
-
HasValue그렇지 않은null경우Value반환true되는 형식bool의 속성입니다. -
TryGetValue매개 변수를 통해 값을 반환bool하고 전달하는 각 사례 형식에 대한 메서드입니다out.
[System.Runtime.CompilerServices.Union]
public struct IntOrBool : System.Runtime.CompilerServices.IUnion
{
private readonly int _intValue;
private readonly bool _boolValue;
private readonly byte _tag; // 0 = none, 1 = int, 2 = bool
public IntOrBool(int? value)
{
if (value.HasValue)
{
_intValue = value.Value;
_tag = 1;
}
}
public IntOrBool(bool? value)
{
if (value.HasValue)
{
_boolValue = value.Value;
_tag = 2;
}
}
public object? Value => _tag switch
{
1 => _intValue,
2 => _boolValue,
_ => null
};
public bool HasValue => _tag != 0;
public bool TryGetValue(out int value)
{
value = _intValue;
return _tag == 1;
}
public bool TryGetValue(out bool value)
{
value = _boolValue;
return _tag == 2;
}
}
static void NonBoxingExample()
{
IntOrBool val = new IntOrBool((int?)42);
var description = val switch
{
int i => $"int: {i}",
bool b => $"bool: {b}",
};
Console.WriteLine(description); // output: int: 42
}
컴파일러는 boxing 값 형식을 TryGetValueValue 방지하는 패턴 일치를 구현할 때 속성보다 선호합니다.
클래스 기반 공용 구조체 형식
클래스는 공용 구조체 형식일 수도 있습니다. 이 유형의 공용 구조체는 참조 의미 체계 또는 상속이 필요한 경우에 유용합니다.
[System.Runtime.CompilerServices.Union]
public class Result<T> : System.Runtime.CompilerServices.IUnion
{
private readonly object? _value;
public Result(T? value) { _value = value; }
public Result(Exception? value) { _value = value; }
public object? Value => _value;
}
static void ClassUnionExample()
{
Result<string> ok = new Result<string>("success");
Result<string> err = new Result<string>(new InvalidOperationException("failed"));
Console.WriteLine(Describe(ok)); // output: OK: success
Console.WriteLine(Describe(err)); // output: Error: failed
static string Describe(Result<string> result) => result switch
{
string s => $"OK: {s}",
Exception e => $"Error: {e.Message}",
null => "null",
};
}
클래스 기반 공용 구조체의 경우 패턴은 null null 참조와 null Value을 모두 일치합니다.
공용 구조체 구현
다음 특성 및 인터페이스는 컴파일 시간 및 런타임에 공용 구조체 형식을 지원합니다.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public sealed class UnionAttribute : Attribute;
public interface IUnion
{
object? Value { get; }
}
}
컴파일러에서 생성된 공용 구조체 선언은 .IUnion 다음을 사용하여 IUnion런타임에 모든 공용 구조체 값을 확인할 수 있습니다.
if (value is IUnion { Value: null }) { /* the union's value is null */ }
형식을 union 선언하면 컴파일러는 구현하는 구조체를 생성합니다 IUnion. 예를 들어 Pet 선언(public union Pet(Cat, Dog, Bird);)은 다음과 같습니다.
[Union] public struct Pet : IUnion
{
public Pet(Cat value) => Value = value;
public Pet(Dog value) => Value = value;
public Pet(Bird value) => Value = value;
public object? Value { get; }
}
중요합니다
.NET 11 미리 보기 2에서는 이러한 형식이 런타임에 포함되지 않습니다. 공용 구조체 형식을 사용하려면 프로젝트에서 선언해야 합니다. 향후 .NET 미리 보기에 포함됩니다.
C# 언어 사양
자세한 내용은 Unions 기능 사양을 참조하세요.
참고하십시오
.NET