공용 구조체 형식(C# 참조)

공용 구조체 형식은 여러 사례 형식 중 하나일 수 있는 값을 나타냅니다. 공용 구조체는 각 사례 유형에서 암시적 변환, 철저한 패턴 일치 및 향상된 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달리 공용 구조체는 같음, 복제 또는 분해 동작을 추가하지 않습니다. 노조는 "어떤 필드가 있습니까?"보다는 "어떤 경우입니까?"에 초점을 맞춥니다.

공용 구조체 형식은 네임스페이 UnionAttribute 스의 형식 및 IUnion 형식을 System.Runtime.CompilerServices 사용합니다. .NET 11 런타임에는 이러한 형식이 포함됩니다.

공용 구조체 선언

공용 구조체 선언은 이름 및 사례 유형 목록을 지정합니다.

public union Pet(Cat, Dog, Bird);

대/소문 자 object형식은 클래스, 구조체, 인터페이스, 형식 매개 변수, nullable 형식 및 기타 공용 구조체를 포함하여 변환되는 모든 형식일 수 있습니다. 다음 예제에서는 다양한 사례 유형 가능성을 보여 줍니다.

public record class Cat(string Name);
public record class Dog(string Name, string Breed);
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)인 경우 공용 구조체 선언은 값을 상자로 만들고 단일 object? 참조로 저장합니다. 사용자 지정 공용 구조체 형식 은 다른 스토리지 전략을 사용할 수 있습니다.

공용 구조체 선언에는 구조체와 마찬가지로 일부 제한 사항이 적용된 추가 멤버가 있는 본문이 포함될 수 있습니다. 공용 구조체 선언에는 인스턴스 필드, 자동 속성 또는 필드와 유사한 이벤트를 포함할 수 없습니다. 컴파일러가 해당 생성자를 공용 구조체 생성 멤버로 생성하므로 단일 매개 변수를 사용하여 공용 생성자를 선언할 수도 없습니다. 선언하는 모든 생성자는 이니셜라이저를 this(...) 사용하여 직접 또는 간접적으로 생성된 생성자에 위임해야 합니다. 다음 Length 공용 구조체는 두 길이를 TotalMeters 결합하는 메서드와 함께 패턴 일치를 사용하여 모든 사례 형식을 Add 처리하는 속성을 추가합니다.

public record class Meters(double Value);
public record class Feet(double Value);

public union Length(Meters, Feet)
{
    public double TotalMeters => this switch
    {
        Meters m => m.Value,
        Feet f => f.Value * 0.3048,
        _ => throw new InvalidOperationException("The Length has no value."),
    };

    public Length Add(Length other) => new Meters(TotalMeters + other.TotalMeters);
}

공용 구조체 변환

암시적 공용 구조체 변환 은 각 사례 형식에서 공용 구조체 형식으로 존재합니다.

static void BasicConversion()
{
    Pet pet = new Dog("Rex", "Labrador");
    Console.WriteLine(pet.Value); // output: Dog { Name = Rex, Breed = Labrador }

    Pet pet2 = new Cat("Whiskers");
    Console.WriteLine(pet2.Value); // output: Cat { Name = Whiskers }
}

공용 구조체 변환은 생성된 해당 생성자를 호출하여 작동합니다. 동일한 형식에 대해 사용자 정의 암시적 변환 연산자가 있는 경우 사용자 정의 연산자는 공용 구조체 변환보다 우선합니다. 둘 이상의 사례 형식을 원본 값에 동일하게 적용할 수 있는 경우 공용 구조체 변환은 모호하며 컴파일러는 오류를 보고합니다. 변환 우선 순위에 대한 자세한 내용은 기능 사양을 참조하세요.

Null 허용 공용 구조체(T?)로의 공용 구조체 변환은 공용 구조체 형식인 경우에도 T 작동합니다.

static void NullableUnionExample()
{
    Pet? maybePet = new Dog("Buddy", "Beagle");
    Pet? noPet = null;

    Console.WriteLine(Describe(maybePet)); // output: 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",
    };
}

공용 구조체 패턴 일치

공용 구조체 식에서 형식 또는 선언 패턴을 사용하는 경우 패턴은 공용 구조체 인스턴스가 아닌 공용 구조체의 콘텐츠를 테스트합니다. 예를 들어 패턴은 Dog 다음과 같은Dog지 여부를 Value 테스트합니다.

static void PatternMatching()
{
    Pet pet = new Dog("Rex", "Labrador");

    var name = pet switch
    {
        Dog d => d.Name,
        Cat c => c.Name,
        Bird b => b.Name,
    };
    Console.WriteLine(name); // output: Rex
}

패턴의 양식에 따라 테스트하는 값이 결정됩니다.

패턴 공용 구조체 식의 동작
형식 또는 선언 패턴(예: Dog 또는 Dog dog 지정된 형식에 대해 내용을 테스트합니다.
형식이 있는 속성 또는 위치 패턴(예: Dog { Breed: "Labrador" } 형식에 대해 내용을 테스트한 다음 중첩된 패턴을 해당 사례 값에 적용합니다.
형식이 없는 속성 또는 위치 패턴(예: { Name: "Rex" } 공용 구조체 인스턴스의 속성 또는 Deconstruct 메서드를 사용합니다.
목록 패턴 콘텐츠가 아닌 공용 구조체 인스턴스의 개수 및 인덱싱 멤버를 사용합니다.
var 또는 버지 _ 패턴 래핑을 해제하지 않고 들어오는 공용 구조체 인스턴스를 캡처하거나 일치합니다.
상수 또는 관계형 패턴 내용을 테스트합니다. 이 패턴은 null null 공용 구조체 참조 또는 null 허용 래퍼도 처리합니다.

예를 들어 포함된 속성 또는 Dog에 위임하는 읽기 전용 Name 속성을 사용하여 공용 구조체를 Cat 정의 Pet 합니다. 속성 식의 switch 형식 패턴은 내용을 테스트합니다.

public union Pet(Cat, Dog)
{
    public string Name => this switch
    {
        Cat cat => cat.Name,
        Dog dog => dog.Name,
        null => "",
    };
}

형식화된 패턴 Dog { Breed: "Labrador" } 은 콘텐츠를 테스트하고 공용 구조체에 없는 속성인 강아지 Breed를 읽습니다. 형식화되지 않은 패턴 { Name: "Rex" } 은 공용 구조체의 Name 속성을 대신 읽으므로 Rex라는 고양이와도 일치합니다.

Pet pet = new Dog("Rex", "Labrador");
Console.WriteLine(pet is Dog { Breed: "Labrador" }); // output: True
Console.WriteLine(pet is { Name: "Rex" });           // output: True
pet = new Cat("Rex");
Console.WriteLine(pet is Dog { Breed: "Labrador" }); // output: False
Console.WriteLine(pet is { Name: "Rex" });           // output: True

패턴은 not 중첩된 패턴을 부정합니다. 에서 pet is not null중첩 패턴 null 은 콘텐츠를 확인하므로 not null 유용한 값 포함 테스트입니다. 반면, 형식화되지 않은 빈 속성 패턴은 { }공용 구조체 인스턴스를 확인합니다. Null인 경우에도 Value 항상 null을 허용하지 않는 구조체 공용 구조체와 일치합니다.

Pet empty = default;
Console.WriteLine(empty is { });       // output: True
Console.WriteLine(empty is not null);  // output: False
if (empty is var captured)
{
    Console.WriteLine(captured.Value is null); // output: True
}

논리적 패턴은 이러한 차이점을 유지합니다. var pet 에서 var pet and not null들어오는 공용 구조체를 캡처하고 not null null이 아닌 콘텐츠가 있는지 테스트합니다. 패턴은 not null and var petvar 여전히 들어오는 공용 구조 not 체를 캡처합니다. 오른쪽 패턴으로 전달된 값은 변경하지 않습니다. 반면에 Dog and { Breed: "Labrador" }형식 패턴은 일치하는 Dog 속성을 속성 패턴에 전달합니다.

메모

형식 패턴은 하나 이상의 사례 형식과 호환되어야 합니다. Pet 이 문서의 pet is Pet 선언에서는 컴파일러 오류 CS8121을 생성합니다. 해당 사례 형식은 일치하지 Pet않습니다. 공용 구조체 인스턴스의 런타임 테스트가 아닙니다. 콘텐츠를 확인하려면 공용 구조체 자체를 캡처하려면 패턴을 사용합니다 pet is not nullpet.Value is not nullvar.

즉, 형식 및 선언 패턴은 내용을 테스트합니다. 형식이 있는 재귀 패턴도 콘텐츠를 테스트합니다. 형식이 없는 재귀 패턴은 공용 구조체의 멤버를 사용합니다. 이러한 규칙은 식과 switch 식에서 is 동일합니다.

Null 일치

구조체 공용 구조체의 경우 패턴은 null null인지 여부를 Value 확인합니다.

static void NullHandling()
{
    Pet pet = default;
    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
    Console.WriteLine(pet.Value is null); // output: True
}

클래스 기반 공용 구조체의 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 식은 각 사례 형식을 처리할 때 공용 구조체의 사례 형식에 대해 완전합니다. Null 허용 여부 분석에서 내용이 null이 아닌 것으로 간주하는 경우 삭제(_) 또는 var arm이 필요하지 않습니다.

static void PatternMatching()
{
    Pet pet = new Dog("Rex", "Labrador");

    var name = pet switch
    {
        Dog d => d.Name,
        Cat c => c.Name,
        Bird b => b.Name,
    };
    Console.WriteLine(name); // output: Rex
}

null 콘텐츠를 처리하려면 팔 위에 팔을 사용하거나(_) 암을 var 삭제하는 것이 좋습니다null. 패턴은 null 의도를 명시하고 일치하는 암 없이 새 사례 유형을 추가할 때 경고를 유지합니다. A var 또는 삭제 암은 새 사례도 허용하므로 컴파일러는 누락된 사례에 대해 경고할 수 없습니다.

추적된 null 상태가 Value "어쩌면 null"이면 팔이 생략 null 되면 컴파일러도 경고합니다. 예를 들어 NullablePet null 허용 사례 형식이 있으므로 해당 매개 변수의 내용이 null일 수 있습니다.

public union NullablePet(Cat?, Dog, Bird);

static string DescribeNullableCase(NullablePet pet) => pet switch
{
    Cat cat => cat.Name,
    Dog dog => dog.Name,
    Bird bird => bird.Name,
    null => "no pet",
};

이 스위치에서 암을 null 제거하면 경고 CS8655가 생성됩니다.

Nullable 분석은 생성된 공용 구조체에 런타임 시 null이 아닌 콘텐츠가 있음을 보장하지 않습니다. 기본 공용 구조체는 기본 초기화된 필드 또는 배열 요소인 경우를 포함하여 null을 포함합니다. 모든 사례 형식이 null을 허용하지 않는 경우 기본 추적 상태는 Value "null이 아님"이므로 컴파일러에서 생략된 null 암에 대해 경고하지는 않습니다. 기본 콘텐츠가 스위치에 도달할 수 있을 때마다 정확성을 처리 null 합니다.

static void NullHandling()
{
    Pet pet = default;
    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
    Console.WriteLine(pet.Value is null); // output: True
}

이 Pet 예제에서는 이전 코드가 추적된 null 상태를 변경하지 않는 한 arm을 제거 null 하면 누락된 null-arm 경고가 생성되지 않지만 기본값을 받으면 스위치가 throw됩니다 SwitchExpressionException . 이전 is null 테스트는 추적된 상태를 변경하고 경고를 일으킬 수 있습니다. 이 동작은 명확한 할당과 별개입니다. 컴파일러는 할당되지 않은 지역 변수의 읽기를 거부합니다.

null 가능성

컴파일러는 다음 규칙을 통해 공용 구조체 속성의 Value null 상태를 추적합니다.

  • 모든 사례 형식의 Value 기본 null 상태가 "어쩌면 null"인 경우 공용 구조체 속성의 기본 null 상태는 "어쩌면 null"입니다. 그렇지 않으면 기본 null 상태는 "null이 아닙니다."입니다.
  • 생성자 또는 공용 구조체 변환 Value 을 통해 사례 형식에서 공용 구조체 값을 만들 때 들어오는 값의 null 상태를 가져옵니다.
  • 비복싱 액세스 패턴 HasValue 또는 TryGetValue(...) 멤버가 공용 구조체의 콘텐츠를 쿼리할 때 null 상태는 Value 분기에서 true "null이 아님"이 됩니다.

사용자 지정 공용 구조체 형식

컴파일러는 선언을 union 선언으로 struct 변환합니다. 구조체는 특성으로 [System.Runtime.CompilerServices.Union] 표시되고 인터페이스를 IUnion 구현합니다. 여기에는 각 사례 형식 및 Value 속성에 대한 공용 생성자가 포함됩니다. 언어는 해당 생성자를 호출하는 암시적 공용 구조체 변환을 제공합니다. 생성된 양식은 의견이 있습니다. 항상 구조체이고, 항상 값 형식 사례를 상자로 지정하고, 항상 콘텐츠를 .로 object?저장합니다.

기존 형식을 조정하거나, 클래스 기반 공용 구조체를 만들거나, 사용자 지정 스토리지 전략을 사용하거나, interop 지원이 필요한 경우 다른 동작이 필요할 수 있습니다. 공용 구조체 형식을 수동으로 만들 수 있습니다.

특성이 있는 [Union] 모든 클래스 또는 구조체는 기본 공용 구조체 패턴을 따르는 경우 공용 구조체 형식입니다. 기본 공용 구조체 패턴에는 다음이 필요합니다.

  • [Union] 형식의 특성입니다.
  • 하나 이상의 공용 생성자( 각각 값별 또는 in 매개 변수가 하나씩 포함됨). 각 생성자의 매개 변수 형식은 사례 형식을 정의합니다.
  • 접근자가 있는 형식(또는Value)의 public object? 속성입니다object.get

이전의 모든 조합원은 공용이어야 합니다. 컴파일러는 이러한 멤버를 사용하여 공용 구조체 변환, 패턴 일치 및 완전성 검사를 구현합니다. boxing이 아닌 액세스 패턴을 구현하거나 클래스 기반 공용 구조체 형식을 만들 수도 있습니다. 사용자 지정 공용 구조체 형식은 멤버를 더 추가할 수 있습니다.

컴파일러는 사용자 지정 공용 구조체 형식이 다음 동작 규칙을 충족한다고 가정합니다.

  • 소리: Value 항상 기본 구조체 값을 포함하여 사례 유형 중 하나의 값을 반환하거나 반환 null 합니다. 사용자 지정 공용 구조체는 기본 생성된 공용 구조체의 null 내용과 달리 기본값을 null이 아닌 경우로 나타낼 수 있습니다.
  • 안정성: 사례 형식 Value 에서 공용 구조체 값을 만드는 경우 해당 사례 형식(또는 null 입력이 있는 null경우)과 일치합니다.
  • 생성 동등성: 값이 암시적으로 두 개의 서로 다른 대/소문자 형식으로 변환할 수 있는 경우 두 생성 멤버 모두 동일한 관찰 가능한 동작을 생성합니다.
  • 액세스 패턴 일관성: 멤버와 HasValue 멤버(TryGetValue있는 경우)는 직접 확인하는 것과 동일하게 동작합니다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 그렇지 않은 bool경우 true 반환 Value 되는 형식 null 의 속성입니다.
  • TryGetValue 매개 변수를 통해 값을 반환 bool 하고 전달하는 각 사례 형식에 대한 메서드입니다out. TryGetValue는 해당 사례 형식의 null이 아닌 값인 경우에만 true 반환 Value 됩니다. out 매개 변수의 형식은 대/소문자 형식 또는 대/소문자 형식이 nullable 값 형식인 경우 기본 값 형식으로 ID 변환할 수 있습니다.
[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
}

패턴 일치의 경우 컴파일러는 형식 패턴 검사(예: union is T)와 HasValue null 패턴 검사(예: union is null값 형식 사례에 대한 boxing 방지)를 호출 TryGetValue 합니다. 각 멤버는 고유한 패턴 종류에 적용되며 다른 멤버에 대한 대체 항목은 없습니다. 멤버가 누락된 경우 컴파일러는 대신 해당 패턴에 Value 대한 속성을 확인합니다. 자세한 내용은 컴파일러가 패턴 일치를 위한 코드를 생성하는 방법을 참조하세요.

공용 구조체 멤버 공급자

공용 구조체 형식은 직접 포함하고 구현하는 공용 중첩 인터페이스에 IUnionMembers 해당 공용 멤버를 위임할 수 있습니다. 해당 인터페이스는 공용 구조체 멤버 공급자입니다. 컴파일러는 상속된 멤버를 포함하여 공급자 인터페이스에서만 공용 구조체 멤버를 찾습니다. 공용 구조체 형식 자체에만 선언된 멤버는 공용 구조체 멤버로 사용되지 않습니다.

공용 구조체 멤버 공급자를 사용하는 경우 인터페이스는 다음 필수 멤버를 선언해야 합니다.

  • 정적 Create 메서드: 각 사례 형식에 대해 하나씩, 단일 매개 변수와 공용 구조체 형식으로 변환할 수 있는 반환 형식 ID를 사용합니다. 이러한 메서드는 사례 유형을 설정합니다.
  • Value property: 포함된 값을 반환하는 접근자가 get 있는 public object 또는 object? 속성입니다.

필요에 따라 인터페이스에서 이러한 멤버를 선언하여 컴파일러가 보다 효율적인 패턴 일치 코드를 생성할 수 있습니다.

  • TryGetValue 메서드: 각 사례 형식에 대해 하나씩, 사례 형식의 매개 변수와 함께 out 반환 bool 됩니다.
  • HasValue property: 접근자가 get 반환되지 않을 null때 Value 반환되는 true public bool 속성입니다.

컴파일러가 패턴 일치TryGetValue를 위한 코드를 생성하고 HasValue 다양한 종류의 패턴에 적용하는 방법은 다음과 같습니다. 다른 패턴에 대한 대체는 없습니다.

  • 형식 패턴(예: union is T컴파일러)의 경우 컴파일러는 선언될 때 값을 TryGetValue(out T value) 추출하고 boxing 없이 해당 패턴에 적용합니다. 그렇지 않으면 컴파일러가 속성에 패턴을 적용합니다 Value . 이 패턴은 런타임 형식 검사를 T 수행하고 값 형식인 경우 T 값을 확인합니다.
  • Null 패턴(예: union is null컴파일러)의 경우 컴파일러는 공용 구조체에 값이 포함되어 있는지 여부를 테스트하기 위해 선언될 때 호출 HasValue 합니다. 그렇지 않으면 컴파일러가 null 패턴을 속성에 Value 직접 적용합니다.

각 멤버는 독립적으로 선택 사항입니다. 아니요 TryGetValue, 형식 패턴 사용 Value; 사용 안 됨 HasValue, null 패턴 사용 Value. 두 멤버 중 하나를 선언하면 컴파일러가 해당 패턴 종류에 대해 보다 효율적이고 강력한 형식의 코드를 생성할 수 있습니다.

[System.Runtime.CompilerServices.Union]
public struct Outcome<T> : Outcome<T>.IUnionMembers
{
    private readonly object? _value;

    private Outcome(object? value) => _value = value;

    public interface IUnionMembers
    {
        static Outcome<T> Create(T? value) => new(value);
        static Outcome<T> Create(Exception? value) => new(value);
        object? Value { get; }

        // Optional but recommended: TryGetValue enables efficient pattern matching
        bool TryGetValue(out T value);
        bool TryGetValue(out Exception value);
    }

    object? IUnionMembers.Value => _value;

    public bool TryGetValue(out T value)
    {
        if (_value is T t)
        {
            value = t;
            return true;
        }
        value = default!;
        return false;
    }

    public bool TryGetValue(out Exception value)
    {
        if (_value is Exception e)
        {
            value = e;
            return true;
        }
        value = default!;
        return false;
    }
}

공용 구조체 멤버 공급자는 공용 구조체 형식에 프라이빗 생성자가 필요하거나 만들기 논리에 공용 구조체 형식과 record class 같은 팩터리 패턴이 필요한 경우에 유용합니다.

클래스 기반 공용 구조체 형식

클래스는 공용 구조체 형식일 수도 있습니다. 이 유형의 공용 구조체는 참조 의미 체계 또는 상속이 필요한 경우에 유용합니다.

[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을 모두 일치합니다.

C# 언어 사양

자세한 내용은 Unions 기능 사양을 참조하세요.

참고하십시오