Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# RSCG - 255 Examples of Roslyn Source Code Generators / 16 created by Microsoft /
# RSCG - 256 Examples of Roslyn Source Code Generators / 16 created by Microsoft /

The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 255 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports.
The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 256 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports.

This system serves as both a learning resource for .NET developers interested in source generators and an automated pipeline for maintaining up-to-date documentation about the RSCG ecosystem

## Latest Update : 2026-02-13 => 13 February 2026
## Latest Update : 2026-02-14 => 14 February 2026

If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)***

Expand All @@ -24,8 +24,30 @@ If you want to be notified each time I add a new RSCG example , please click htt

## Content

Those are the 255 Roslyn Source Code Generators that I have tested you can see and download source code example.
Those are the 256 Roslyn Source Code Generators that I have tested you can see and download source code example.
( including 16 from Microsoft )
### 256. [Sundew.DiscriminatedUnions](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions) , in the [FunctionalProgramming](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#functionalprogramming) category

Generated on : 2026-02-14 => 14 February 2026

<details>
<summary>Expand</summary>



Author: Kim Hugener Ohlsen



Nuget: [https://www.nuget.org/packages/Sundew.DiscriminatedUnions/](https://www.nuget.org/packages/Sundew.DiscriminatedUnions/)


Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions)

Source: [https://github.com/sundews/Sundew.DiscriminatedUnions](https://github.com/sundews/Sundew.DiscriminatedUnions)

</details>

### 255. [KnockOff](https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff) , in the [Tests](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#tests) category

Generated on : 2026-02-13 => 13 February 2026
Expand Down
2 changes: 1 addition & 1 deletion later.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Just later

## Latest Update : 2026-02-13 => 13 February 2026
## Latest Update : 2026-02-14 => 14 February 2026



Expand Down
1 change: 1 addition & 0 deletions v2/Generator/MultiGeneratorV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ public string[] SourceNoRSCG()
text = text.Replace("(skills/", $"({d.Generator!.Source}/skills");

text = text.Replace("Access them as a ReadOnlySpan<byte>", "Access them as a ReadOnlySpan\\<byte\\>");
text = text.Replace("<TUnion>", "\\<TUnion\\>");
text = text.Replace("### ", "##### ");
text = text.Replace("## ", "#### ");
text = text.Replace("# ", "### ");
Expand Down
1 change: 1 addition & 0 deletions v2/Generator/all.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions v2/RSCGExamplesData/GeneratorDataRec.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
75 changes: 75 additions & 0 deletions v2/book/examples/Sundew.DiscriminatedUnions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

<h1>RSCG nr 256 : Sundew.DiscriminatedUnions</h1>

<h2>Info</h2>
Nuget : <a href="https://www.nuget.org/packages/Sundew.DiscriminatedUnions/" target="_blank">https://www.nuget.org/packages/Sundew.DiscriminatedUnions/</a>

<p>You can find more details at : <a href="https://github.com/sundews/Sundew.DiscriminatedUnions" target="_blank"> https://github.com/sundews/Sundew.DiscriminatedUnions</a></p>

<p>Author :Kim Hugener Ohlsen</p>

<p>Source: <a href="https://github.com/sundews/Sundew.DiscriminatedUnions" target="_blank">https://github.com/sundews/Sundew.DiscriminatedUnions</a> </p>

<h2>About</h2>

Generate tagged union

<h2>
How to use
</h2>
<h3>
Add reference to the <a href="https://www.nuget.org/packages/Sundew.DiscriminatedUnions/" target="_blank">Sundew.DiscriminatedUnions</a> in the csproj
</h3>
<img src="images/Sundew.DiscriminatedUnions/UnionTypesDemo.csproj.png" width="580" height="580" />

<h3>This was for me the <b>starting</b> code</h3>

<br />
I have <b>coded</b> the file Program.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/csFiles/Program.cs.png" width="580" height="580" />
<hr />

<br />
I have <b>coded</b> the file ResultSave.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/csFiles/ResultSave.cs.png" width="580" height="580" />
<hr />

<br />
I have <b>coded</b> the file SaveToDatabase.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/csFiles/SaveToDatabase.cs.png" width="580" height="580" />
<hr />
<h3>And here are the <i>generated</i> files</h3>

<br />
The file <i>generated</i> is UnionTypesDemo.ResultSave.generated.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/generated/UnionTypesDemo.ResultSave.generated.cs.png" width="580" height="580" />

<br />
The file <i>generated</i> is UnionTypesDemo.ResultSave.NotFound.generated.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/generated/UnionTypesDemo.ResultSave.NotFound.generated.cs.png" width="580" height="580" />

<br />
The file <i>generated</i> is UnionTypesDemo.ResultSave.Ok.generated.cs
<br />
<img src="images/Sundew.DiscriminatedUnions/generated/UnionTypesDemo.ResultSave.Ok.generated.cs.png" width="580" height="580" />

<p>
You can download the code and this page as pdf from
<a target="_blank" href='https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions'>
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Sundew.DiscriminatedUnions
</a>
</p>


<p>
You can see the whole list at
<a target="_blank" href='https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG'>
https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG
</a>
</p>

6 changes: 5 additions & 1 deletion v2/book/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</head>
<body>
<h1>
This is the list of 255 RSCG with examples =>
This is the list of 256 RSCG with examples =>
</h1>

<table >
Expand Down Expand Up @@ -1046,6 +1046,10 @@ <h1>
<td>255</td>
<td><a href="examples/KnockOff.html">KnockOff</a></td>
</tr>
<tr>
<td>256</td>
<td><a href="examples/Sundew.DiscriminatedUnions.html">Sundew.DiscriminatedUnions</a></td>
</tr>
</table>


Expand Down
1 change: 1 addition & 0 deletions v2/book/pandocHTML.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 22 additions & 0 deletions v2/rscg_examples/Sundew.DiscriminatedUnions/description.json
Original file line number Diff line number Diff line change
@@ -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":""
}
}
130 changes: 130 additions & 0 deletions v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor typos in upstream readme content.

Line 75 contains "convienient" → "convenient" and "chucks" → "chunks". Since this file appears to be a cached copy of the upstream README (fetched by GrabReadMe), these typos originate from the Sundew.DiscriminatedUnions repository. Consider reporting upstream if the documentation is rendered as-is on your site.

🧰 Tools
🪛 LanguageTool

[grammar] ~75-~75: Ensure spelling is correct
Context: ..., but it is not required. This makes it convienient to separate handling logic in smaller c...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~75-~75: Ensure spelling is correct
Context: ...t to separate handling logic in smaller chucks of code. ```csharp public int Evaluate...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In `@v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt` at line 75, Fix the
two typos in the cached README content: replace "convienient" with "convenient"
and "chucks" with "chunks" in the string on the line containing "This makes it
convienient to separate handling logic in smaller chucks of code." in
v2/rscg_examples/Sundew.DiscriminatedUnions/readme.txt, and optionally add a
note or open an issue in the Sundew.DiscriminatedUnions upstream repo to report
the original README typos so they can be corrected at source.


```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<TUnion> 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
Original file line number Diff line number Diff line change
@@ -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"

}
Loading