");
text = text.Replace("### ", "##### ");
text = text.Replace("## ", "#### ");
text = text.Replace("# ", "### ");
diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv
index 7623fc5a3..cf6e6e590 100644
--- a/v2/Generator/all.csv
+++ b/v2/Generator/all.csv
@@ -254,3 +254,4 @@ Nr,Key,Source,Category
253,FastCloner, https://github.com/lofcz/FastCloner/,Clone
254,ErrorOrX, https://github.com/ANcpLua/ErrorOrX,API
255,KnockOff, https://github.com/NeatooDotNet/KnockOff,Tests
+256,Sundew.DiscriminatedUnions, https://github.com/sundews/Sundew.DiscriminatedUnions,FunctionalProgramming
diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json
index baa03019d..06f285fc0 100644
--- a/v2/RSCGExamplesData/GeneratorDataRec.json
+++ b/v2/RSCGExamplesData/GeneratorDataRec.json
@@ -1546,5 +1546,11 @@
"Category": 13,
"dtStart": "2026-02-13T00:00:00",
"show": true
+ },
+ {
+ "ID":"Sundew.DiscriminatedUnions",
+ "Category": 10,
+ "dtStart": "2026-02-14T00:00:00",
+ "show": true
}
]
\ No newline at end of file
diff --git a/v2/book/examples/Sundew.DiscriminatedUnions.html b/v2/book/examples/Sundew.DiscriminatedUnions.html
new file mode 100644
index 000000000..20f69d704
--- /dev/null
+++ b/v2/book/examples/Sundew.DiscriminatedUnions.html
@@ -0,0 +1,75 @@
+
+RSCG nr 256 : Sundew.DiscriminatedUnions
+
+Info
+Nuget : https://www.nuget.org/packages/Sundew.DiscriminatedUnions/
+
+You can find more details at : https://github.com/sundews/Sundew.DiscriminatedUnions
+
+Author :Kim Hugener Ohlsen
+
+Source: https://github.com/sundews/Sundew.DiscriminatedUnions
+
+About
+
+Generate tagged union
+
+
+ How to use
+
+
+
+
+This was for me the starting code
+
+
+ I have coded the file Program.cs
+
+
+
+
+
+ I have coded the file ResultSave.cs
+
+
+
+
+
+ I have coded the file SaveToDatabase.cs
+
+
+
+ And here are the generated files
+
+
+ The file generated is UnionTypesDemo.ResultSave.generated.cs
+
+
+
+
+ The file generated is UnionTypesDemo.ResultSave.NotFound.generated.cs
+
+
+
+
+ The file generated is UnionTypesDemo.ResultSave.Ok.generated.cs
+
+
+
+
+ You can download the code and this page as pdf from
+
+ https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions
+
+
+
+
+
+ You can see the whole list at
+
+ https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG
+
+
+
diff --git a/v2/book/list.html b/v2/book/list.html
index 71c549521..a77aa33b4 100644
--- a/v2/book/list.html
+++ b/v2/book/list.html
@@ -17,7 +17,7 @@
-This is the list of 255 RSCG with examples =>
+This is the list of 256 RSCG with examples =>
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml
index be7df0edb..53156b762 100644
--- a/v2/book/pandocHTML.yaml
+++ b/v2/book/pandocHTML.yaml
@@ -269,6 +269,7 @@ input-files:
- examples/FastCloner.html
- examples/ErrorOrX.html
- examples/KnockOff.html
+- examples/Sundew.DiscriminatedUnions.html
# or you may use input-file: with a single value
# defaults:
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/description.json b/v2/rscg_examples/Sundew.DiscriminatedUnions/description.json
new file mode 100644
index 000000000..16a858449
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/description.json
@@ -0,0 +1,22 @@
+{
+ "generator":{
+ "name":"Sundew.DiscriminatedUnions",
+ "nuget":[
+ "https://www.nuget.org/packages/Sundew.DiscriminatedUnions/"
+ ],
+ "link":"https://github.com/sundews/Sundew.DiscriminatedUnions",
+ "author":"Kim Hugener Ohlsen",
+ "source":"https://github.com/sundews/Sundew.DiscriminatedUnions"
+ },
+ "data":{
+ "goodFor":["Generate tagged union"],
+ "csprojDemo":"UnionTypesDemo.csproj",
+ "csFiles":["Program.cs","ResultSave.cs","SaveToDatabase.cs"],
+ "excludeDirectoryGenerated":[""],
+ "includeAdditionalFiles":[""]
+ },
+ "links":{
+ "blog":"",
+ "video":""
+ }
+}
\ No newline at end of file
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt b/v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt
new file mode 100644
index 000000000..9fdb619a8
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt
@@ -0,0 +1,130 @@
+# Discriminated Unions
+
+Sundew.DiscriminatedUnions implement discriminated unions for C#, until a future version of C# provides it out of the box.
+The idea is that this package can be deleted once unions are supported in C#, without requiring changes to switch expressions and statements.
+
+In addition, the project supports dimensional unions through default interface methods (traits).
+A dimensional union is a union where cases can be reused in any number of unions, by supporting interface unions through the possibility of implementing multiple interface and default interface members.
+
+## How it works
+A Roslyn analyzer asserts and report errors in case switch statements or switch expression do not handle all cases.
+C# 8 and 9 already comes with great pattern matching support for evaluation.
+
+In order that the inheritance hierarchy remain closed (All cases in the same assembly), an analyzer ensures that unions are not derived from in referencing assemblies.
+Similarly all case classes should be sealed.
+
+Create a union by inheriting from an abstract base (record) class (or interface) marked with the DiscriminatedUnion attribute to build various cases.
+Either specify the partial keyword to the union for a source generator to implement factory methods or use the codefix PDU0001 to generate them.
+
+## Sample
+### Defining a union
+```csharp
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public abstract partial record Result
+{
+ public sealed partial record Success : Result;
+
+ public sealed partial record Warning(string Message) : Result;
+
+ public sealed partial record Error(int Code) : Result;
+}
+```
+Alternatively, a union can be defined with unnested case classes and interfaces, allowing the possibility of creating dimensional unions (see below).
+
+### Evaluation
+```csharp
+var message = result switch
+{
+ Result.Error { Code: > 70 } error => $"High Error code: {error.Code}",
+ Result.Error error => $"Error code: {error.Code}",
+ Result.Warning { Message: "Tough warning" } => "Not good",
+ Result.Warning warning => warning.Message,
+ Result.Success => "Great",
+};
+```
+
+### Dimensional unions
+To support dimensional unions, unnested cases help because the cases are no longer defined inside a union. However, for this to work the unions are required to declare a factory method named exactly like the case type and that has the CaseType attribute specifying the actual type.
+Since version 3, factory methods are generated when the union is declared partial. Alternatively, a code fix (PDU0001) is available to generate the factory methods.
+
+```csharp
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface IExpression;
+
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface IArithmeticExpression : IExpression;
+
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface ICommutativeExpression : IArithmeticExpression;
+
+public sealed partial record AdditionExpression(IExpression Lhs, IExpression Rhs) : ICommutativeExpression;
+
+public sealed partial record SubtractionExpression(IExpression Lhs, IExpression Rhs) : IArithmeticExpression;
+
+public sealed partial record MultiplicationExpression(IExpression Lhs, IExpression Rhs) : ICommutativeExpression;
+
+public sealed partial record DivisionExpression(IExpression Lhs, IExpression Rhs) : IArithmeticExpression;
+
+public sealed partial record ValueExpression(int Value) : IExpression;
+```
+
+#### Evaluating dimensional unions
+With dimensional unions it is possible to handle all cases using a sub union.
+As seen in the example below, handling the ArithmeticExpression covers Addition-, Subtraction-, Multiplication- and DivisionExpression.
+Typically one would dispatch these to a method handling ArithmeticExpression and where handling all cases would be checked, but it is not required.
+This makes it convienient to separate handling logic in smaller chucks of code.
+
+```csharp
+public int Evaluate(Expression expression)
+{
+ return expression switch
+ {
+ ArithmeticExpression arithmeticExpression => Evaluate(arithmeticExpression),
+ ValueExpression valueExpression => valueExpression.Value,
+ };
+}
+
+public int Evaluate(ArithmeticExpression arithmeticExpression)
+{
+ return arithmeticExpression switch
+ {
+ AdditionExpression additionExpression => Evaluate(additionExpression.Lhs) + Evaluate(additionExpression.Rhs),
+ SubtractionExpression subtractionExpression => Evaluate(subtractionExpression.Lhs) - Evaluate(subtractionExpression.Rhs),
+ MultiplicationExpression multiplicationExpression => Evaluate(multiplicationExpression.Lhs) * Evaluate(multiplicationExpression.Rhs),
+ DivisionExpression divisionExpression => Evaluate(divisionExpression.Lhs) / Evaluate(divisionExpression.Rhs),
+ };
+}
+```
+
+#### Enum evaluation
+As of version 5.1, regular enums can also use the DiscriminatedUnion attribute causing the analyzer to exhaustively check switch statements and expressions.
+
+## Generator features
+As mentioned a source generator is automatically activated for generating factory methods when the partial keyword is specified.
+In addition, the DiscriminatedUnion attribute can specify a flags enum (GeneratorFeatures) to control additional code generation.
+
+* Segregate - Generates an extension method for IEnumerable that segregates all items into buckets of the different result.
+
+## Supported diagnostics:
+| Diagnostic Id | Description | Code Fix |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------- | :------: |
+| SDU0001 | Switch does not handled all cases | yes |
+| SDU0002 | Switch should not handle default case | yes |
+| SDU0003 | Switch has unreachable null case | yes |
+| SDU0004 | Class unions must be abstract | yes |
+| SDU0005 | Only unions can extended other unions | no |
+| SDU0006 | Unions cannot be extended outside their assembly | no |
+| SDU0007 | Cases must be declared in the same assembly as their unions | no |
+| SDU0008 | Cases should be sealed | yes |
+| SDU0009 | Unnested cases should have factory method | PDU0001 |
+| SDU0010 | Factory method should have correct CaseTypeAttribute | yes |
+| SDU0011 | Reported when a case is implemented by throwing NotImplementedException, because CodeCleanup may siliently 'fix' SDU0001. | yes |
+| SDU0012 | Reported when a case contains type parameters that are not in the union type parameter list. | yes |
+| PDU0001 | Make union/case partial for code generator | yes |
+| PDU0002 | Populate union factory methods | yes |
+| SDU9999 | Switch should throw in default case | no |
+| GDU0001 | Discriminated union declaration could not be found | no |
+
+## Issues/Todos
+* Switch appears with red squiggly lines in VS: https://github.com/dotnet/roslyn/issues/57041
+* Nullability is falsely evaluated when the switch hints null is possible: https://github.com/dotnet/roslyn/issues/57042
\ No newline at end of file
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/.tours/Sundew.DiscriminatedUnions.tour b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/.tours/Sundew.DiscriminatedUnions.tour
new file mode 100644
index 000000000..42a4d0de8
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/.tours/Sundew.DiscriminatedUnions.tour
@@ -0,0 +1,54 @@
+
+{
+ "$schema": "https://aka.ms/codetour-schema",
+ "title": "Sundew.DiscriminatedUnions",
+ "steps":
+ [
+ {
+ "file": "UnionTypesDemo/UnionTypesDemo.csproj",
+ "description": "First, we add Nuget [Sundew.DiscriminatedUnions](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/) in csproj ",
+ "pattern": "Sundew.DiscriminatedUnions"
+ }
+
+ ,{
+ "file": "UnionTypesDemo/SaveToDatabase.cs",
+ "description": "File SaveToDatabase.cs ",
+ "pattern": "this is the code"
+ }
+
+ ,{
+ "file": "UnionTypesDemo/ResultSave.cs",
+ "description": "File ResultSave.cs ",
+ "pattern": "this is the code"
+ }
+
+ ,{
+ "file": "UnionTypesDemo/Program.cs",
+ "description": "File Program.cs \r\n>> dotnet run --project UnionTypesDemo/UnionTypesDemo.csproj ",
+ "pattern": "this is the code"
+ }
+
+
+ ,{
+ "file": "UnionTypesDemo/obj/GX/Sundew.DiscriminatedUnions.Generator/Sundew.DiscriminatedUnions.Generator.DiscriminatedUnionGenerator/UnionTypesDemo.ResultSave.Ok.generated.cs",
+ "description": "Generated File 3 from 3 : UnionTypesDemo.ResultSave.Ok.generated.cs ",
+ "line": 1
+ }
+
+ ,{
+ "file": "UnionTypesDemo/obj/GX/Sundew.DiscriminatedUnions.Generator/Sundew.DiscriminatedUnions.Generator.DiscriminatedUnionGenerator/UnionTypesDemo.ResultSave.NotFound.generated.cs",
+ "description": "Generated File 2 from 3 : UnionTypesDemo.ResultSave.NotFound.generated.cs ",
+ "line": 1
+ }
+
+ ,{
+ "file": "UnionTypesDemo/obj/GX/Sundew.DiscriminatedUnions.Generator/Sundew.DiscriminatedUnions.Generator.DiscriminatedUnionGenerator/UnionTypesDemo.ResultSave.generated.cs",
+ "description": "Generated File 1 from 3 : UnionTypesDemo.ResultSave.generated.cs ",
+ "line": 1
+ }
+
+ ],
+
+ "ref": "main"
+
+}
\ No newline at end of file
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo.sln b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo.sln
new file mode 100644
index 000000000..d7021d30f
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34221.43
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnionTypesDemo", "UnionTypesDemo\UnionTypesDemo.csproj", "{B3F52D30-D0FB-4DF2-B494-6B903B16613D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B3F52D30-D0FB-4DF2-B494-6B903B16613D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3F52D30-D0FB-4DF2-B494-6B903B16613D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3F52D30-D0FB-4DF2-B494-6B903B16613D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3F52D30-D0FB-4DF2-B494-6B903B16613D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7C63CD9F-20A2-4EAB-9766-127A7184E23B}
+ EndGlobalSection
+EndGlobal
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/Program.cs b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/Program.cs
new file mode 100644
index 000000000..8a4f87cd4
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/Program.cs
@@ -0,0 +1,17 @@
+using UnionTypesDemo;
+
+Console.WriteLine("Save or not");
+ResultSave data = SaveToDatabase.Save(0);
+var message= data switch
+{
+ ResultSave.Ok ok => $"Saved {ok.i}",
+ ResultSave.NotFound => "Not found",
+};
+Console.WriteLine(message);
+data = SaveToDatabase.Save(1);
+message = data switch
+{
+ ResultSave.Ok ok => $"Saved {ok.i}",
+ ResultSave.NotFound => "Not found",
+};
+Console.WriteLine(message);
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/ResultSave.cs b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/ResultSave.cs
new file mode 100644
index 000000000..5ec237fe6
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/ResultSave.cs
@@ -0,0 +1,16 @@
+
+using Sundew.DiscriminatedUnions;
+
+namespace UnionTypesDemo;
+
+
+[DiscriminatedUnion]
+public abstract partial record ResultSave
+{
+
+ public sealed partial record Ok(int i) : ResultSave;
+
+ public sealed partial record NotFound():ResultSave ;
+
+}
+
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/SaveToDatabase.cs b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/SaveToDatabase.cs
new file mode 100644
index 000000000..1d58dea12
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/SaveToDatabase.cs
@@ -0,0 +1,16 @@
+namespace UnionTypesDemo;
+
+public class SaveToDatabase
+{
+ public static ResultSave Save(int i)
+ {
+
+ if (i == 0)
+ {
+ return ResultSave._NotFound;
+ }
+ return ResultSave._Ok(i);
+ }
+}
+
+
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/UnionTypesDemo.csproj b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/UnionTypesDemo.csproj
new file mode 100644
index 000000000..afacc6819
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/src/UnionTypesDemo/UnionTypesDemo.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
+
+
+
+
+
+
diff --git a/v2/rscg_examples/Sundew.DiscriminatedUnions/video.json b/v2/rscg_examples/Sundew.DiscriminatedUnions/video.json
new file mode 100644
index 000000000..0284d5bfb
--- /dev/null
+++ b/v2/rscg_examples/Sundew.DiscriminatedUnions/video.json
@@ -0,0 +1,39 @@
+{
+ "scriptName": "Sundew.DiscriminatedUnions",
+ "steps":
+[
+ {"typeStep":"exec","arg":"clipchamp.exe launch"},
+ {"typeStep":"text","arg": "Welcome to Roslyn Examples"},
+ {"typeStep":"text","arg":"If you want to see more examples , see List Of RSCG"},
+ {"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG"},
+ {"typeStep":"text","arg": "My name is Andrei Ignat and I am deeply fond of Roslyn Source Code Generator. "},
+
+{"typeStep":"text","arg": "Today I will present Sundew.DiscriminatedUnions . Generate tagged union ."},
+{"typeStep":"browser","arg":"https://www.nuget.org/packages/Sundew.DiscriminatedUnions/"},
+{"typeStep":"text","arg": "The whole example is here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions"},
+{"typeStep":"text","arg": "You can download the code from here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions#download-example-net--c-"},
+{"typeStep":"text","arg":"Here is the code downloaded "},
+{"typeStep":"exec","arg":"explorer.exe /select,D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Sundew.DiscriminatedUnions\\src\\UnionTypesDemo.sln"},
+{"typeStep":"text","arg": "So , let's start the project with Visual Studio Code "},
+{"typeStep":"stepvscode","arg": "-n D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Sundew.DiscriminatedUnions\\src"},
+
+{"typeStep":"text","arg": "To use it ,you will put the Nuget Sundew.DiscriminatedUnions into the csproj "},
+
+{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Sundew.DiscriminatedUnions\\src\\UnionTypesDemo\\UnionTypesDemo.csproj"},
+
+{"typeStep":"text","arg": "And now I will show you an example of using Sundew.DiscriminatedUnions"},
+
+{"typeStep":"hide","arg": "now execute the tour in VSCode"},
+{"typeStep":"tour", "arg": "src/.tours/"},
+{"typeStep":"text","arg":" And I will execute the project"},
+{"typeStep":"showproj", "arg":"UnionTypesDemo.csproj"},
+{"typeStep":"text","arg":" This concludes the project"},
+{"typeStep":"waitseconds","arg":"30"},
+{"typeStep":"text","arg": "Remember, you can download the code from here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions#download-example-net--c-",
+SpeakTest=" "},
+{"typeStep":"waitseconds","arg":"30"},
+]
+}
diff --git a/v2/rscg_examples_site/docs/Authors/Kim_Hugener_Ohlsen.md b/v2/rscg_examples_site/docs/Authors/Kim_Hugener_Ohlsen.md
new file mode 100644
index 000000000..88921ce4f
--- /dev/null
+++ b/v2/rscg_examples_site/docs/Authors/Kim_Hugener_Ohlsen.md
@@ -0,0 +1,7 @@
+# Author : Kim Hugener Ohlsen
+
+Number RSCG: 1
+
+
+ 1 [Sundew.DiscriminatedUnions](/docs/Sundew.DiscriminatedUnions) [](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/)  2026-02-14
+
diff --git a/v2/rscg_examples_site/docs/Categories/FunctionalProgramming.md b/v2/rscg_examples_site/docs/Categories/FunctionalProgramming.md
index 4e83f5f8a..05ad2a145 100644
--- a/v2/rscg_examples_site/docs/Categories/FunctionalProgramming.md
+++ b/v2/rscg_examples_site/docs/Categories/FunctionalProgramming.md
@@ -1,6 +1,6 @@
FunctionalProgramming
-Number RSCG: 16
+Number RSCG: 17
1 [cachesourcegenerator](/docs/cachesourcegenerator) [](https://www.nuget.org/packages/cachesourcegenerator/)  2024-02-14
@@ -28,9 +28,11 @@ Number RSCG: 16
13 [Sera.Union](/docs/Sera.Union) [](https://www.nuget.org/packages/Sera.Union/)  2024-08-26
- 14 [TypeUtilities](/docs/TypeUtilities) [](https://www.nuget.org/packages/TypeUtilities/)  2024-03-05
+ 14 [Sundew.DiscriminatedUnions](/docs/Sundew.DiscriminatedUnions) [](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/)  2026-02-14
- 15 [UnionGen](/docs/UnionGen) [](https://www.nuget.org/packages/UnionGen/)  2024-04-05
+ 15 [TypeUtilities](/docs/TypeUtilities) [](https://www.nuget.org/packages/TypeUtilities/)  2024-03-05
- 16 [UnionsGenerator](/docs/UnionsGenerator) [](https://www.nuget.org/packages/RhoMicro.CodeAnalysis.UnionsGenerator)  2024-02-18
+ 16 [UnionGen](/docs/UnionGen) [](https://www.nuget.org/packages/UnionGen/)  2024-04-05
+
+ 17 [UnionsGenerator](/docs/UnionsGenerator) [](https://www.nuget.org/packages/RhoMicro.CodeAnalysis.UnionsGenerator)  2024-02-18
\ No newline at end of file
diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveFunctionalProgramming.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveFunctionalProgramming.mdx
index ba68dbd1f..b61057fce 100644
--- a/v2/rscg_examples_site/docs/Categories/_PrimitiveFunctionalProgramming.mdx
+++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveFunctionalProgramming.mdx
@@ -26,11 +26,13 @@
13 [Sera.Union](/docs/Sera.Union) [](https://www.nuget.org/packages/Sera.Union/)  2024-08-26
- 14 [TypeUtilities](/docs/TypeUtilities) [](https://www.nuget.org/packages/TypeUtilities/)  2024-03-05
+ 14 [Sundew.DiscriminatedUnions](/docs/Sundew.DiscriminatedUnions) [](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/)  2026-02-14
- 15 [UnionGen](/docs/UnionGen) [](https://www.nuget.org/packages/UnionGen/)  2024-04-05
+ 15 [TypeUtilities](/docs/TypeUtilities) [](https://www.nuget.org/packages/TypeUtilities/)  2024-03-05
- 16 [UnionsGenerator](/docs/UnionsGenerator) [](https://www.nuget.org/packages/RhoMicro.CodeAnalysis.UnionsGenerator)  2024-02-18
+ 16 [UnionGen](/docs/UnionGen) [](https://www.nuget.org/packages/UnionGen/)  2024-04-05
+
+ 17 [UnionsGenerator](/docs/UnionsGenerator) [](https://www.nuget.org/packages/RhoMicro.CodeAnalysis.UnionsGenerator)  2024-02-18
### See category
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/Sundew.DiscriminatedUnions.md b/v2/rscg_examples_site/docs/RSCG-Examples/Sundew.DiscriminatedUnions.md
new file mode 100644
index 000000000..6d575878a
--- /dev/null
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/Sundew.DiscriminatedUnions.md
@@ -0,0 +1,433 @@
+---
+sidebar_position: 2560
+title: 256 - Sundew.DiscriminatedUnions
+description: Generate tagged union
+slug: /Sundew.DiscriminatedUnions
+---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import TOCInline from '@theme/TOCInline';
+import SameCategory from '../Categories/_PrimitiveFunctionalProgramming.mdx';
+
+# Sundew.DiscriminatedUnions by Kim Hugener Ohlsen
+
+
+
+
+## NuGet / site data
+[](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/)
+[](https://github.com/sundews/Sundew.DiscriminatedUnions)
+
+
+## Details
+
+### Info
+:::info
+
+Name: **Sundew.DiscriminatedUnions**
+
+
+
+Author: Kim Hugener Ohlsen
+
+NuGet:
+*https://www.nuget.org/packages/Sundew.DiscriminatedUnions/*
+
+
+You can find more details at https://github.com/sundews/Sundew.DiscriminatedUnions
+
+Source: https://github.com/sundews/Sundew.DiscriminatedUnions
+
+:::
+
+### Author
+:::note
+Kim Hugener Ohlsen
+
+:::
+
+## Original Readme
+:::note
+
+### Discriminated Unions
+
+Sundew.DiscriminatedUnions implement discriminated unions for C#, until a future version of C# provides it out of the box.
+The idea is that this package can be deleted once unions are supported in C#, without requiring changes to switch expressions and statements.
+
+In addition, the project supports dimensional unions through default interface methods (traits).
+A dimensional union is a union where cases can be reused in any number of unions, by supporting interface unions through the possibility of implementing multiple interface and default interface members.
+
+###### How it works
+A Roslyn analyzer asserts and report errors in case switch statements or switch expression do not handle all cases.
+C# 8 and 9 already comes with great pattern matching support for evaluation.
+
+In order that the inheritance hierarchy remain closed (All cases in the same assembly), an analyzer ensures that unions are not derived from in referencing assemblies.
+Similarly all case classes should be sealed.
+
+Create a union by inheriting from an abstract base (record) class (or interface) marked with the DiscriminatedUnion attribute to build various cases.
+Either specify the partial keyword to the union for a source generator to implement factory methods or use the codefix PDU0001 to generate them.
+
+###### Sample
+######### Defining a union
+```csharp
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public abstract partial record Result
+{
+ public sealed partial record Success : Result;
+
+ public sealed partial record Warning(string Message) : Result;
+
+ public sealed partial record Error(int Code) : Result;
+}
+```
+Alternatively, a union can be defined with unnested case classes and interfaces, allowing the possibility of creating dimensional unions (see below).
+
+######### Evaluation
+```csharp
+var message = result switch
+{
+ Result.Error \{ Code: > 70 \} error => $"High Error code: {error.Code}",
+ Result.Error error => $"Error code: {error.Code}",
+ Result.Warning \{ Message: "Tough warning" \} => "Not good",
+ Result.Warning warning => warning.Message,
+ Result.Success => "Great",
+};
+```
+
+######### Dimensional unions
+To support dimensional unions, unnested cases help because the cases are no longer defined inside a union. However, for this to work the unions are required to declare a factory method named exactly like the case type and that has the CaseType attribute specifying the actual type.
+Since version 3, factory methods are generated when the union is declared partial. Alternatively, a code fix (PDU0001) is available to generate the factory methods.
+
+```csharp
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface IExpression;
+
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface IArithmeticExpression : IExpression;
+
+[Sundew.DiscriminatedUnions.DiscriminatedUnion]
+public partial interface ICommutativeExpression : IArithmeticExpression;
+
+public sealed partial record AdditionExpression(IExpression Lhs, IExpression Rhs) : ICommutativeExpression;
+
+public sealed partial record SubtractionExpression(IExpression Lhs, IExpression Rhs) : IArithmeticExpression;
+
+public sealed partial record MultiplicationExpression(IExpression Lhs, IExpression Rhs) : ICommutativeExpression;
+
+public sealed partial record DivisionExpression(IExpression Lhs, IExpression Rhs) : IArithmeticExpression;
+
+public sealed partial record ValueExpression(int Value) : IExpression;
+```
+
+########## Evaluating dimensional unions
+With dimensional unions it is possible to handle all cases using a sub union.
+As seen in the example below, handling the ArithmeticExpression covers Addition-, Subtraction-, Multiplication- and DivisionExpression.
+Typically one would dispatch these to a method handling ArithmeticExpression and where handling all cases would be checked, but it is not required.
+This makes it convienient to separate handling logic in smaller chucks of code.
+
+```csharp
+public int Evaluate(Expression expression)
+{
+ return expression switch
+ {
+ ArithmeticExpression arithmeticExpression => Evaluate(arithmeticExpression),
+ ValueExpression valueExpression => valueExpression.Value,
+ };
+}
+
+public int Evaluate(ArithmeticExpression arithmeticExpression)
+{
+ return arithmeticExpression switch
+ {
+ AdditionExpression additionExpression => Evaluate(additionExpression.Lhs) + Evaluate(additionExpression.Rhs),
+ SubtractionExpression subtractionExpression => Evaluate(subtractionExpression.Lhs) - Evaluate(subtractionExpression.Rhs),
+ MultiplicationExpression multiplicationExpression => Evaluate(multiplicationExpression.Lhs) * Evaluate(multiplicationExpression.Rhs),
+ DivisionExpression divisionExpression => Evaluate(divisionExpression.Lhs) / Evaluate(divisionExpression.Rhs),
+ };
+}
+```
+
+########## Enum evaluation
+As of version 5.1, regular enums can also use the DiscriminatedUnion attribute causing the analyzer to exhaustively check switch statements and expressions.
+
+###### Generator features
+As mentioned a source generator is automatically activated for generating factory methods when the partial keyword is specified.
+In addition, the DiscriminatedUnion attribute can specify a flags enum (GeneratorFeatures) to control additional code generation.
+
+* Segregate - Generates an extension method for IEnumerable\ that segregates all items into buckets of the different result.
+
+###### Supported diagnostics:
+| Diagnostic Id | Description | Code Fix |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------- | :------: |
+| SDU0001 | Switch does not handled all cases | yes |
+| SDU0002 | Switch should not handle default case | yes |
+| SDU0003 | Switch has unreachable null case | yes |
+| SDU0004 | Class unions must be abstract | yes |
+| SDU0005 | Only unions can extended other unions | no |
+| SDU0006 | Unions cannot be extended outside their assembly | no |
+| SDU0007 | Cases must be declared in the same assembly as their unions | no |
+| SDU0008 | Cases should be sealed | yes |
+| SDU0009 | Unnested cases should have factory method | PDU0001 |
+| SDU0010 | Factory method should have correct CaseTypeAttribute | yes |
+| SDU0011 | Reported when a case is implemented by throwing NotImplementedException, because CodeCleanup may siliently 'fix' SDU0001. | yes |
+| SDU0012 | Reported when a case contains type parameters that are not in the union type parameter list. | yes |
+| PDU0001 | Make union/case partial for code generator | yes |
+| PDU0002 | Populate union factory methods | yes |
+| SDU9999 | Switch should throw in default case | no |
+| GDU0001 | Discriminated union declaration could not be found | no |
+
+###### Issues/Todos
+* Switch appears with red squiggly lines in VS: https://github.com/dotnet/roslyn/issues/57041
+* Nullability is falsely evaluated when the switch hints null is possible: https://github.com/dotnet/roslyn/issues/57042
+
+:::
+
+### About
+:::note
+
+Generate tagged union
+
+
+:::
+
+## How to use
+
+### Example (source csproj, source files)
+
+
+
+
+
+This is the CSharp Project that references **Sundew.DiscriminatedUnions**
+```xml showLineNumbers {16}
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+
+ This is the use of **Sundew.DiscriminatedUnions** in *Program.cs*
+
+```csharp showLineNumbers
+using UnionTypesDemo;
+
+Console.WriteLine("Save or not");
+ResultSave data = SaveToDatabase.Save(0);
+var message= data switch
+{
+ ResultSave.Ok ok => $"Saved {ok.i}",
+ ResultSave.NotFound => "Not found",
+};
+Console.WriteLine(message);
+data = SaveToDatabase.Save(1);
+message = data switch
+{
+ ResultSave.Ok ok => $"Saved {ok.i}",
+ ResultSave.NotFound => "Not found",
+};
+Console.WriteLine(message);
+
+```
+
+
+
+
+ This is the use of **Sundew.DiscriminatedUnions** in *ResultSave.cs*
+
+```csharp showLineNumbers
+
+using Sundew.DiscriminatedUnions;
+
+namespace UnionTypesDemo;
+
+
+[DiscriminatedUnion]
+public abstract partial record ResultSave
+{
+
+ public sealed partial record Ok(int i) : ResultSave;
+
+ public sealed partial record NotFound():ResultSave ;
+
+}
+
+
+```
+
+
+
+
+ This is the use of **Sundew.DiscriminatedUnions** in *SaveToDatabase.cs*
+
+```csharp showLineNumbers
+namespace UnionTypesDemo;
+
+public class SaveToDatabase
+{
+ public static ResultSave Save(int i)
+ {
+
+ if (i == 0)
+ {
+ return ResultSave._NotFound;
+ }
+ return ResultSave._Ok(i);
+ }
+}
+
+
+
+```
+
+
+
+
+### Generated Files
+
+Those are taken from $(BaseIntermediateOutputPath)\GX
+
+
+
+
+```csharp showLineNumbers
+#nullable enable
+
+namespace UnionTypesDemo
+{
+#pragma warning disable SA1601
+ [global::System.Diagnostics.DebuggerNonUserCode]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Sundew.DiscriminateUnions.Generator", "6.0.0.0")]
+ public partial record ResultSave : global::Sundew.DiscriminatedUnions.IDiscriminatedUnion
+#pragma warning restore SA1601
+ {
+ ///
+ /// Gets the NotFound case.
+ ///
+ /// The NotFound.
+ [Sundew.DiscriminatedUnions.CaseType(typeof(global::UnionTypesDemo.ResultSave.NotFound))]
+ public static global::UnionTypesDemo.ResultSave _NotFound \{ get; }
+ = new global::UnionTypesDemo.ResultSave.NotFound();
+
+ ///
+ /// Factory method for the Ok case.
+ ///
+ /// The i.
+ /// A new Ok.
+ [Sundew.DiscriminatedUnions.CaseType(typeof(global::UnionTypesDemo.ResultSave.Ok))]
+ public static global::UnionTypesDemo.ResultSave _Ok(int i)
+ => new global::UnionTypesDemo.ResultSave.Ok(i);
+
+ ///
+ /// Gets all cases in the union.
+ ///
+ /// A readonly list of types.
+ public static global::System.Collections.Generic.IReadOnlyList Cases \{ get; }
+ = new global::System.Type[] \{ typeof(global::UnionTypesDemo.ResultSave.NotFound), typeof(global::UnionTypesDemo.ResultSave.Ok) };
+ }
+}
+
+```
+
+
+
+
+```csharp showLineNumbers
+#nullable enable
+
+namespace UnionTypesDemo
+{
+ public partial record ResultSave
+ {
+#pragma warning disable SA1601
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Sundew.DiscriminateUnions.Generator", "6.0.0.0")]
+ public partial record NotFound
+#pragma warning restore SA1601
+ {
+ ///
+ /// Gets all cases in the union.
+ ///
+ /// A readonly list of types.
+ public new static global::System.Collections.Generic.IReadOnlyList Cases \{ get; }
+ = new global::System.Type[] \{ typeof(global::UnionTypesDemo.ResultSave.NotFound) };
+ }
+ }
+}
+
+```
+
+
+
+
+```csharp showLineNumbers
+#nullable enable
+
+namespace UnionTypesDemo
+{
+ public partial record ResultSave
+ {
+#pragma warning disable SA1601
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Sundew.DiscriminateUnions.Generator", "6.0.0.0")]
+ public partial record Ok
+#pragma warning restore SA1601
+ {
+ ///
+ /// Gets all cases in the union.
+ ///
+ /// A readonly list of types.
+ public new static global::System.Collections.Generic.IReadOnlyList Cases \{ get; }
+ = new global::System.Type[] \{ typeof(global::UnionTypesDemo.ResultSave.Ok) };
+ }
+ }
+}
+
+```
+
+
+
+
+## Useful
+
+### Download Example (.NET C#)
+
+:::tip
+
+[Download Example project Sundew.DiscriminatedUnions ](/sources/Sundew.DiscriminatedUnions.zip)
+
+:::
+
+
+### Share Sundew.DiscriminatedUnions
+
+
+
+https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions
+
+
+
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md
index dac55d475..c7b8c6b00 100644
--- a/v2/rscg_examples_site/docs/RSCG-Examples/index.md
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/index.md
@@ -1,7 +1,7 @@
---
sidebar_position: 30
-title: 255 RSCG list by category
-description: 255 RSCG list by category
+title: 256 RSCG list by category
+description: 256 RSCG list by category
slug: /rscg-examples
---
@@ -954,7 +954,7 @@ import DocCardList from '@theme/DocCardList';
## FunctionalProgramming
- Expand FunctionalProgramming =>examples:16
+ Expand FunctionalProgramming =>examples:17
@@ -1035,6 +1035,11 @@ import DocCardList from '@theme/DocCardList';
[rscg_demeter](/docs/rscg_demeter)
+
+
+
+[Sundew.DiscriminatedUnions](/docs/Sundew.DiscriminatedUnions)
+
@@ -1949,6 +1954,8 @@ flowchart LR;
FunctionalProgramming--> rscg_demeter((rscg_demeter))
+ FunctionalProgramming--> Sundew.DiscriminatedUnions((Sundew.DiscriminatedUnions))
+
Hangfire--> HangfireRecurringJob((HangfireRecurringJob))
Idempotency--> RSCG_idempotency((RSCG_idempotency))
diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md
index b81a91fe8..d16c4e722 100644
--- a/v2/rscg_examples_site/docs/about.md
+++ b/v2/rscg_examples_site/docs/about.md
@@ -6,7 +6,7 @@ title: About
## Content
You will find here code examples
-of 255 Roslyn Source Code Generator (RSCG)
+of 256 Roslyn Source Code Generator (RSCG)
that can be useful for you. That means, you will write more elegant and concise code - even if the generators code is not always nice to look.
## Are those examples ready for production?
diff --git a/v2/rscg_examples_site/docs/indexRSCG.md b/v2/rscg_examples_site/docs/indexRSCG.md
index 539881405..72222025b 100644
--- a/v2/rscg_examples_site/docs/indexRSCG.md
+++ b/v2/rscg_examples_site/docs/indexRSCG.md
@@ -7,9 +7,9 @@ slug: /List-of-RSCG
import useBaseUrl from '@docusaurus/useBaseUrl';
-## 255 RSCG with examples in descending chronological order
+## 256 RSCG with examples in descending chronological order
-This is the list of 255 ( 16 from Microsoft) RSCG with examples
+This is the list of 256 ( 16 from Microsoft) RSCG with examples
[See by category](/docs/rscg-examples) [See as json](/exports/RSCG.json) [See as Excel](/exports/RSCG.xlsx)
@@ -20,6 +20,7 @@ This is the list of 255 ( 16 from Microsoft) RSCG with examples
| No | Name | Date | Category |
| --------- | ----- | ---- | -------- |
+|256| [Sundew.DiscriminatedUnions by Kim Hugener Ohlsen ](/docs/Sundew.DiscriminatedUnions)|2026-02-14 => 14 February 2026 | [FunctionalProgramming](/docs/Categories/FunctionalProgramming) |
|255| [KnockOff by Keith Voels ](/docs/KnockOff)|2026-02-13 => 13 February 2026 | [Tests](/docs/Categories/Tests) |
|254| [ErrorOrX by Alexander Nachtmanns ](/docs/ErrorOrX)|2026-02-02 => 02 February 2026 | [API](/docs/Categories/API) |
|253| [FastCloner by Matěj Štágl ](/docs/FastCloner)|2026-02-01 => 01 February 2026 | [Clone](/docs/Categories/Clone) |
diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
index a4510b61c..b7a13c155 100644
--- a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
+++ b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
@@ -4,7 +4,7 @@ import styles from './styles.module.css';
const FeatureList = [
{
-title: '255 Examples (16 from MSFT)',
+title: '256 Examples (16 from MSFT)',
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
description: (
<>
diff --git a/v2/rscg_examples_site/static/exports/RSCG.json b/v2/rscg_examples_site/static/exports/RSCG.json
index 39a8a25cc..6ee1a8f19 100644
--- a/v2/rscg_examples_site/static/exports/RSCG.json
+++ b/v2/rscg_examples_site/static/exports/RSCG.json
@@ -2041,6 +2041,14 @@
"Source": "https://github.com/NeatooDotNet/KnockOff",
"Category": "Tests",
"AddedOn": "2026-02-13T00:00:00"
+ },
+ {
+ "Name": "Sundew.DiscriminatedUnions",
+ "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions",
+ "NuGet": "https://www.nuget.org/packages/Sundew.DiscriminatedUnions/",
+ "Source": "https://github.com/sundews/Sundew.DiscriminatedUnions",
+ "Category": "FunctionalProgramming",
+ "AddedOn": "2026-02-14T00:00:00"
}
]
}
\ No newline at end of file
diff --git a/v2/rscg_examples_site/static/exports/RSCG.xlsx b/v2/rscg_examples_site/static/exports/RSCG.xlsx
index 71ca47d9d..5b5c8c091 100644
Binary files a/v2/rscg_examples_site/static/exports/RSCG.xlsx and b/v2/rscg_examples_site/static/exports/RSCG.xlsx differ
diff --git a/v2/rscg_examples_site/static/sources/Sundew.DiscriminatedUnions.zip b/v2/rscg_examples_site/static/sources/Sundew.DiscriminatedUnions.zip
new file mode 100644
index 000000000..66f6f2f47
Binary files /dev/null and b/v2/rscg_examples_site/static/sources/Sundew.DiscriminatedUnions.zip differ