From d7be71ba3b6f205f9ffc3cc1e2a34b12b9a52504 Mon Sep 17 00:00:00 2001 From: Barion Date: Wed, 29 Jul 2026 18:24:56 +0200 Subject: [PATCH 1/7] Update union.md clarify Union member providers --- docs/csharp/language-reference/builtin-types/union.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/csharp/language-reference/builtin-types/union.md b/docs/csharp/language-reference/builtin-types/union.md index 760b376c8e80e..5b6d8cdd5e9c9 100644 --- a/docs/csharp/language-reference/builtin-types/union.md +++ b/docs/csharp/language-reference/builtin-types/union.md @@ -171,6 +171,9 @@ The compiler prefers `TryGetValue` over the `Value` property when implementing p ### Union member providers +> [!NOTE] +> If a `IUnionMembers` interface is used all union members must be defined on that interface + A union type can delegate its union members to a nested `IUnionMembers` interface. When this interface is present, the compiler looks for `Create` factory methods instead of constructors: :::code language="csharp" source="snippets/unions/MemberProvider.cs" id="MemberProvider"::: From d695346cb0f0fffeae6b62fc59b058beeeedd071 Mon Sep 17 00:00:00 2001 From: Barion Date: Wed, 29 Jul 2026 18:31:26 +0200 Subject: [PATCH 2/7] Update union.md --- docs/csharp/language-reference/builtin-types/union.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/language-reference/builtin-types/union.md b/docs/csharp/language-reference/builtin-types/union.md index 5b6d8cdd5e9c9..33d9288766d83 100644 --- a/docs/csharp/language-reference/builtin-types/union.md +++ b/docs/csharp/language-reference/builtin-types/union.md @@ -172,7 +172,7 @@ The compiler prefers `TryGetValue` over the `Value` property when implementing p ### Union member providers > [!NOTE] -> If a `IUnionMembers` interface is used all union members must be defined on that interface +> If an `IUnionMembers` interface is used, all union members must be defined on that interface. A union type can delegate its union members to a nested `IUnionMembers` interface. When this interface is present, the compiler looks for `Create` factory methods instead of constructors: From 66a58afed2278642c7a0bb877eac3da0f774e9cc Mon Sep 17 00:00:00 2001 From: Barion Date: Tue, 4 Aug 2026 07:24:27 +0200 Subject: [PATCH 3/7] Apply suggestion from @BillWagner Co-authored-by: Bill Wagner --- docs/csharp/language-reference/builtin-types/union.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/union.md b/docs/csharp/language-reference/builtin-types/union.md index 33d9288766d83..303176b117410 100644 --- a/docs/csharp/language-reference/builtin-types/union.md +++ b/docs/csharp/language-reference/builtin-types/union.md @@ -171,10 +171,7 @@ The compiler prefers `TryGetValue` over the `Value` property when implementing p ### Union member providers -> [!NOTE] -> If an `IUnionMembers` interface is used, all union members must be defined on that interface. - -A union type can delegate its union members to a nested `IUnionMembers` interface. When this interface is present, the compiler looks for `Create` factory methods instead of constructors: +A union type can delegate its union members to a nested `IUnionMembers` interface. When this interface is present, the `union` type behaves as a *union member provider*. The compiler generates code to call the `IUnionMembers` interface. It won't generate calls to members declared on the union type that aren't members of the nested `IUnionMembers` interface. As the following example shows, that means you must add the necessary factory methods and the appropriate `TryGetValue` methods for all case types: :::code language="csharp" source="snippets/unions/MemberProvider.cs" id="MemberProvider"::: From 1230ccd4d4f0705f5866ca95bac6f9c60428b82b Mon Sep 17 00:00:00 2001 From: Barion Date: Tue, 4 Aug 2026 07:48:20 +0200 Subject: [PATCH 4/7] Update MemberProvider.cs --- .../snippets/unions/MemberProvider.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs index 7daa52d08cb6d..cc0715e421acc 100644 --- a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs +++ b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs @@ -8,12 +8,38 @@ public record class Outcome : Outcome.IUnionMembers public interface IUnionMembers { - static Outcome Create(T? value) => new(value); - static Outcome Create(Exception? value) => new(value); + public static Outcome Create(T? value) => new(value: value); + public static Outcome Create(Exception? value) => new(value: value); object? Value { get; } + + // only when needed + 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; + } } // From 5d799b69c13afca7b520b5d0619f14d5673abf6a Mon Sep 17 00:00:00 2001 From: Barion Date: Tue, 4 Aug 2026 07:49:18 +0200 Subject: [PATCH 5/7] Update MemberProvider.cs --- .../builtin-types/snippets/unions/MemberProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs index cc0715e421acc..8a653e7e4f49d 100644 --- a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs +++ b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs @@ -8,8 +8,8 @@ public record class Outcome : Outcome.IUnionMembers public interface IUnionMembers { - public static Outcome Create(T? value) => new(value: value); - public static Outcome Create(Exception? value) => new(value: value); + static Outcome Create(T? value) => new(value: value); + static Outcome Create(Exception? value) => new(value: value); object? Value { get; } // only when needed From 4fdcbaeec87462fa457f780f5bc71946aa5c165d Mon Sep 17 00:00:00 2001 From: Barion Date: Tue, 4 Aug 2026 20:52:29 +0200 Subject: [PATCH 6/7] Update MemberProvider.cs --- .../builtin-types/snippets/unions/MemberProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs index 8a653e7e4f49d..29c53306f5499 100644 --- a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs +++ b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs @@ -8,8 +8,8 @@ public record class Outcome : Outcome.IUnionMembers public interface IUnionMembers { - static Outcome Create(T? value) => new(value: value); - static Outcome Create(Exception? value) => new(value: value); + static Outcome Create(T? value) => new((object?)value); + static Outcome Create(Exception? value) => new((object?)value); object? Value { get; } // only when needed From 909e111e2132947ac6ad331ff157cde632afbf21 Mon Sep 17 00:00:00 2001 From: Barion Date: Wed, 5 Aug 2026 10:35:42 +0200 Subject: [PATCH 7/7] Update MemberProvider.cs --- .../builtin-types/snippets/unions/MemberProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs index 29c53306f5499..c5b01ab8ef560 100644 --- a/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs +++ b/docs/csharp/language-reference/builtin-types/snippets/unions/MemberProvider.cs @@ -1,6 +1,6 @@ // [System.Runtime.CompilerServices.Union] -public record class Outcome : Outcome.IUnionMembers +public struct Outcome : Outcome.IUnionMembers { private readonly object? _value; @@ -8,8 +8,8 @@ public record class Outcome : Outcome.IUnionMembers public interface IUnionMembers { - static Outcome Create(T? value) => new((object?)value); - static Outcome Create(Exception? value) => new((object?)value); + static Outcome Create(T? value) => new(value); + static Outcome Create(Exception? value) => new(value); object? Value { get; } // only when needed