meetings/working-groups/discriminated-unions/union-proposals-overview.md
Umbrella issue: https://github.com/dotnet/csharplang/issues/9662
flowchart LR
%% Features
Unions[Unions]:::approved
Standard[Standard type unions]:::approved
Enums[Closed enums]:::approved
Hierarchies[Closed hierarchies]:::approved
Cases[Case declarations]:::approved
TargetAccess[Target-typed access]:::approved
TargetInfer[Target-typed inference]:::approved
InferNew[Inference for constructors]:::approved
InferPattern[Inference for type patterns]:::consideration
TypeValue[Type value conversion]:::consideration
%% Dependencies
Unions --> Standard
Unions & Hierarchies --> Cases -.-> TargetAccess
Hierarchies <-.-> Enums
TargetInfer --> InferNew & InferPattern
%% Colors
classDef approved fill:#cfc,stroke:#333,stroke-width:1.5px;
classDef consideration fill:#ffd,stroke:#333,stroke-width:1.5px;
classDef unapproved fill:#ddd,stroke:#333,stroke-width:1.5px;
classDef rejected fill:#fdd,stroke:#333,stroke-width:1.5px;
A set of interlinked features that combine to provide C# support for union types, including a declaration syntax and several useful behaviors.
public union Pet(Cat, Dog); // Declaration syntax
Pet pet = dog; // Implicit conversion
_ = pet switch
{
Cat cat => ..., // Implicit matching
Dog dog => ...,
} // Exhaustive switching
A family of nominal type unions in the System namespace:
public union Union<T1, T2>(T1, T2);
public union Union<T1, T2, T3>(T1, T2, T3);
public union Union<T1, T2, T3, T4>(T1, T2, T3, T4);
...
Allow enums to be declared closed, preventing creation of values other than the explicitly declared enum members. A consuming switch expression can assume only those values can occur, avoiding exhaustiveness warnings.
public closed enum Color { Red, Green, Blue }
var infrared = Color.Red - 1; // Error, not a declared member
_ = color switch
{
Red => "red",
Green => "green",
Blue => "blue"
// No warning about missing cases
};
Allow classes to be declared closed, preventing its use as a base class outside of the assembly. A consuming switch expression can assume only derived types from within that assembly can occur, avoiding exhaustiveness warnings.
// Assembly 1
public closed class C { ... }
public class D : C { ... } // Ok, same assembly
// Assembly 2
public class E : C { ... } // Error, 'C' is closed and in a different assembly
_ = c switch
{
D d => ...,
// No warning about missing cases
}
A shorthand for declaring nested case types of a closed type (closed class or union type).
public closed record GateState
{
case Closed;
case Locked;
case Open(float Percent);
}
public union Pet
{
case Cat(...);
case Dog(...);
}
Enables a type name to be omitted from static member access when it is the same as the target type.
return result switch
{
.Success(var val) => val,
.Error => defaultVal,
};
Generic type inference may take a target type into account.
MyCollection<string> c = MyCollection.Create(); // 'T' = 'string' inferred from target type
'new' expressions may infer type arguments for the newly created class or struct, including from a target type if present.
Option<int> option = new Some(5); // Infer 'int' from argument and target type
Type patterns may omit a type argument list when it can be inferred from the pattern input value.
void M(Option<int> option) => option switch
{
Some(var i) => ..., // 'Some<int>' inferred
...
};
A type expression specified in a value context can be converted to a value if the type supports a conversion to value.
GateState value = GateState.Locked;