<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Damir's Corner]]></title>
        <description><![CDATA[Notes from Daily Encounters with Technology]]></description>
        <link>https://www.damirscorner.com</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Fri, 09 Oct 2026 06:17:36 GMT</lastBuildDate>
        <atom:link href="https://www.damirscorner.com/blog/posts/rss.xml" rel="self" type="application/rss+xml"/>
        <author><![CDATA[Damir Arh]]></author>
        <pubDate>Fri, 09 Oct 2026 06:15:48 GMT</pubDate>
        <item>
            <title><![CDATA[Refactor tests to make them more readable]]></title>
            <description><![CDATA[<p>The key role of tests is to ensure that the software under test behaves correctly. You first add tests to verify that you have correctly implemented a feature. The same tests then serve as a safety net so that you don&#39;t inadvertently break existing functionality.</p>
<p>But there&#39;s also value in test code being easy to read and understand. When a test breaks you want to quickly see what it is doing and what failed. And that is not going to happen if the test code is long and unstructured. And the only way to achieve that is to treat your test code the same way as your production code: as its complexity is growing you should refactor it.</p>
<p>Let&#39;s take a look at a test from <a href="20261002-UsingRequestTimeoutsMiddlewareInWebApi.html">my previous blog post</a>:</p>
<pre class="highlight"><code class="hljs csharp">[Test]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">SlowResponseAfterPointOfNoReturnTest</span><span class="hljs-params">()</span>
</span>{
    SetUpApiResponses(TimeSpan.FromSeconds(<span class="hljs-number">0.6</span>), TimeSpan.FromSeconds(<span class="hljs-number">0.6</span>));

    <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> httpClient = _factory.CreateClient();

    <span class="hljs-keyword">var</span> stopwatch = Stopwatch.StartNew();
    <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> httpClient.PostAsync(
        <span class="hljs-string">"/submissions"</span>,
        <span class="hljs-keyword">new</span> StringContent(<span class="hljs-keyword">string</span>.Empty)
    );
    stopwatch.Stop();
    response.EnsureSuccessStatusCode();
    <span class="hljs-keyword">var</span> submissionFromCreate = <span class="hljs-keyword">await</span> response
        .Content
        .ReadFromJsonAsync&lt;Submission&gt;();
    Assert.That(stopwatch.ElapsedMilliseconds, Is.InRange(<span class="hljs-number">1000</span>, <span class="hljs-number">1100</span>));
    Assert.That(submissionFromCreate, Is.Not.Null);
    Assert.That(submissionFromCreate.Id, Is.Not.EqualTo(Guid.Empty));
    Assert.That(submissionFromCreate.Phase1CompletedAt, Is.Not.Null);
    Assert.That(submissionFromCreate.Phase2CompletedAt, Is.Null);

    <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">1</span>));

    <span class="hljs-keyword">var</span> submissionFromGet = <span class="hljs-keyword">await</span> httpClient.GetFromJsonAsync&lt;Submission&gt;(
        $<span class="hljs-string">"/submissions/{submissionFromCreate.Id}"</span>
    );
    Assert.That(submissionFromGet, Is.Not.Null);
    Assert.That(submissionFromGet.Id, Is.EqualTo(submissionFromCreate.Id));
    Assert.That(submissionFromGet.Phase1CompletedAt, Is.Not.Null);
    Assert.That(submissionFromGet.Phase2CompletedAt, Is.Not.Null);
}
</code></pre>
<p>The test is rather short but there is still some effort required to fully understand what is happening. The code in between the assertion blocks has many responsibilities: it initializes the test case, it makes REST calls, it measures time. This makes it difficult to see what is the actual test scenario.</p>
<p>With some refactoring this can be significantly improved:</p>
<pre class="highlight"><code class="hljs csharp">[Test]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">SlowResponseAfterPointOfNoReturnTest</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">var</span> scenario = <span class="hljs-keyword">new</span> SubmissionScenario(
        _application,
        TimeSpan.FromSeconds(<span class="hljs-number">0.6</span>),
        TimeSpan.FromSeconds(<span class="hljs-number">0.6</span>)
    );

    <span class="hljs-keyword">var</span> submissionFromCreate = <span class="hljs-keyword">await</span> scenario.CreateSubmissionAsync();
    Assert.That(scenario.LastResponseTimeMilliseconds, Is.InRange(<span class="hljs-number">1000</span>, <span class="hljs-number">1100</span>));
    Assert.That(submissionFromCreate, Is.Not.Null);
    Assert.That(submissionFromCreate.Id, Is.Not.EqualTo(Guid.Empty));
    Assert.That(submissionFromCreate.Phase1CompletedAt, Is.Not.Null);
    Assert.That(submissionFromCreate.Phase2CompletedAt, Is.Null);

    <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">1</span>));

    <span class="hljs-keyword">var</span> submissionFromGet = <span class="hljs-keyword">await</span> scenario.GetSubmissionAsync();
    Assert.That(submissionFromGet, Is.Not.Null);
    Assert.That(submissionFromGet.Id, Is.EqualTo(submissionFromCreate.Id));
    Assert.That(submissionFromGet.Phase1CompletedAt, Is.Not.Null);
    Assert.That(submissionFromGet.Phase2CompletedAt, Is.Not.Null);
}
</code></pre>
<p>I have moved the low-level code for making REST calls and measuring response times into separate clearly-named methods. It&#39;s now much easier to see that the test only creates a submission and retrieves it after a short delay.</p>
<p>The new helper methods hide all the low-level implementation details:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Submission?&gt; CreateSubmissionAsync()
{
    <span class="hljs-keyword">var</span> stopwatch = Stopwatch.StartNew();
    <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> _httpClient.PostAsync(
        <span class="hljs-string">"/submissions"</span>,
        <span class="hljs-keyword">new</span> StringContent(<span class="hljs-keyword">string</span>.Empty)
    );
    stopwatch.Stop();
    LastResponseStatusCode = response.StatusCode;
    LastResponseTimeMilliseconds = stopwatch.ElapsedMilliseconds;
    <span class="hljs-keyword">if</span> (!response.IsSuccessStatusCode)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
    <span class="hljs-keyword">var</span> submission = <span class="hljs-keyword">await</span> response.Content.ReadFromJsonAsync&lt;Submission&gt;();
    <span class="hljs-keyword">if</span> (submission != <span class="hljs-keyword">null</span>)
    {
        _lastSubmissionId = submission.Id;
    }
    <span class="hljs-keyword">return</span> submission;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Submission?&gt; GetSubmissionAsync()
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _httpClient.GetFromJsonAsync&lt;Submission&gt;(
        $<span class="hljs-string">"/submissions/{_lastSubmissionId}"</span>
    );
}
</code></pre>
<p>The methods return only REST responses and store all the other details as local state for easy access from assertions. Even the submission ID is stored locally so that you don&#39;t need pass it from one method to the other.</p>
<p>I&#39;ve also moved all the setup code from the test class:</p>
<ul>
<li><p>The setup method to the same scenario class that hosts the two methods above:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">SubmissionScenario</span> : <span class="hljs-title">IDisposable</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> WebApplicationUnderTest _application;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> HttpClient _httpClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">SubmissionScenario</span><span class="hljs-params">(
        WebApplicationUnderTest application,
        TimeSpan phase1Delay,
        TimeSpan phase2Delay
    )</span>
    </span>{
        _application = application;
        _httpClient = _application.Factory.CreateClient();
        SetUpApiResponses(phase1Delay, phase2Delay);
    }

    <span class="hljs-comment">// ...</span>

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Dispose</span><span class="hljs-params">()</span>
    </span>{
        _httpClient.Dispose();
    }
}
</code></pre>
</li>
<li><p>And the code for starting the web application under test to a separate class so that it could be used from different scenario classes:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">WebApplicationUnderTest</span> : <span class="hljs-title">IDisposable</span>
{
    <span class="hljs-keyword">public</span> WebApplicationFactory&lt;Program&gt; Factory { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> WireMockServer Server { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">WebApplicationUnderTest</span><span class="hljs-params">()</span>
    </span>{
        Server = WireMockServer.Start();
        Factory = <span class="hljs-keyword">new</span> WebApplicationFactory&lt;Program&gt;()
            .WithWebHostBuilder(builder =&gt;
            {
                builder.ConfigureAppConfiguration(
                    (_, config) =&gt;
                    {
                        config.AddInMemoryCollection(
                            <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>?&gt; {
                                [<span class="hljs-string">"BaseAddress"</span>] = Server.Url
                            }
                        );
                    }
                );
            });
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Dispose</span><span class="hljs-params">()</span>
    </span>{
        Factory.Dispose();
        Server.Dispose();
    }
}
</code></pre>
</li>
</ul>
<p>This way the test class is only responsible for the test cases it contains. And it&#39;s now much easier to share the helper code between multiple test classes if needed without resorting to inheritance and deriving all test classes from a common base class.</p>
<p>I suggest you clone the test project from my <a href="https://github.com/DamirsCorner/20261009-dotnet-test-helpers">GitHub repository</a> and look at it in your favorite IDE. It should make it much easier to see how the code is structured. You can also check how I improved the other tests in the project. And even inspect the exact changes I made to the original unstructured tests since those are still available in the repository as a separate commit.</p>
<p>With some minimum refactoring effort I managed to make the tests much more readable. Of course, further improvements could be made:</p>
<ul>
<li>The long blocks of assertions which repeat a lot across all tests could be moved to custom assertions.</li>
<li>A separate builder class for the scenario would make the initialization code even easier to understand, especially as the number of parameter grows.</li>
</ul>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20261009-RefactorTestsToMakeThemMoreReadable.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20261009-RefactorTestsToMakeThemMoreReadable.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Using request timeouts middleware in web API]]></title>
            <description><![CDATA[<p>Last year a wrote <a href="20250725-LimitWebApiResponseTime.html">a blog post</a> about restricting a REST API endpoint total response time but potentially continuing the processing in the background. I implemented it fully by hand without any external dependencies. But the code could be simplified by taking advantage of <a href="https://learn.microsoft.com/en-us/aspnet/core/performance/timeouts">the request timeouts middleware</a>.</p>
<p>The core of my original solution was a method with the following structure:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Guid&gt; <span class="hljs-title">CreateAsync</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">var</span> submission = <span class="hljs-keyword">new</span> Submission(Guid.NewGuid(), <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);

    <span class="hljs-keyword">var</span> cancellationTokenSource = <span class="hljs-keyword">new</span> CancellationTokenSource();
    cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(<span class="hljs-number">1</span>));
    Task[] tasks =
    [
        ProcessAsync(submission, cancellationTokenSource.Token),
        Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">1</span>), CancellationToken.None),
    ];
    <span class="hljs-keyword">await</span> Task.WhenAny(tasks);

    <span class="hljs-keyword">return</span> submission.Id;
}
</code></pre>
<p>The same timeout period was specified in two places:</p>
<ul>
<li>the <code>CancellationTokenSource</code> and in turn its cancellation token which is passed into the actual worker method <code>ProcessAsync</code>, and</li>
<li>the <code>Task.Delay</code> call which defines how long we wait for the worker method to complete.</li>
</ul>
<p>The request timeouts middleware hooks into the <code>HttpContext.RequestAborted</code> cancellation token and makes sure that it requests cancellation not only when the underlying connection is aborted but also when the specified timeout expires.</p>
<p>So instead of creating the cancellation token inside the wrapper method, it should now be passes into the method from the controller:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Guid&gt; <span class="hljs-title">CreateAsync</span><span class="hljs-params">(CancellationToken cancellationToken)</span>
</span>{
    <span class="hljs-keyword">var</span> submission = <span class="hljs-keyword">new</span> Submission(Guid.NewGuid(), <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);

    Task[] tasks =
    [
        ProcessAsync(submission, cancellationToken),
        Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">1</span>), CancellationToken.None),
    ];
    <span class="hljs-keyword">await</span> Task.WhenAny(tasks);

    <span class="hljs-keyword">return</span> submission.Id;
}
</code></pre>
<p>But even after this change, we still rely on a separate <code>Task.Delay</code> call to stop waiting for the <code>ProcessAsync</code> method to complete. We don&#39;t want that because the delay value is defined independently from the request timeout configuration.</p>
<p>Fortunately, there is a simpler way to stop waiting for <code>ProcessAsync</code> to complete and keep it running in the background which I could have used even in my original solution:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Guid&gt; <span class="hljs-title">CreateAsync</span><span class="hljs-params">(CancellationToken cancellationToken)</span>
</span>{
    <span class="hljs-keyword">var</span> submission = <span class="hljs-keyword">new</span> Submission(Guid.NewGuid(), <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);
    <span class="hljs-keyword">try</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">await</span> <span class="hljs-title">ProcessAsync</span><span class="hljs-params">(submission, cancellationToken)</span>
            .<span class="hljs-title">WaitAsync</span><span class="hljs-params">(cancellationToken)</span></span>;
    }
    <span class="hljs-keyword">catch</span> (OperationCanceledException)
        when (cancellationToken.IsCancellationRequested)
    {
        <span class="hljs-comment">// The request timeout elapsed,</span>
        <span class="hljs-comment">// but processing may continue in the background</span>
    }
    <span class="hljs-keyword">return</span> submission.Id;
}
</code></pre>
<p>The task returned by <code>WaitAsync</code> will complete either when the original task (i.e., the <code>ProcessAsync</code> method) completes or when the cancellation token requests the cancellation, whichever comes first. Which matches exactly what I achieved in the original implementation using <code>Task.Delay</code>.</p>
<p>Notice how I need to ignore the <code>OperationCanceledException</code> thrown when awaiting the <code>WaitAsync</code> method. Otherwise the exception would have been thrown from this method instead of the value being returned.</p>
<p>Of course, there is some setup required to use the request timeouts middleware:</p>
<pre class="highlight"><code class="hljs csharp">builder.Services.AddRequestTimeouts();
<span class="hljs-comment">// ...</span>
app.UseRequestTimeouts();
</code></pre>
<p>And in the controller, the cancellation token has to be retrieved and passed along:</p>
<pre class="highlight"><code class="hljs csharp">[HttpPost]
[RequestTimeout(<span class="hljs-number">1000</span>)]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;IActionResult&gt; <span class="hljs-title">Create</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">var</span> submissionId = <span class="hljs-keyword">await</span> submissionsService.CreateAsync(
        HttpContext.RequestAborted
    );
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>In this particular case, I specified the request timeout with an attribute on the action method, but I could also do it at the controller level or with a global default.</p>
<p>I have a fully working sample project in my <a href="https://github.com/DamirsCorner/20261002-aspnet-request-timeouts">GitHub repository</a>. The tests cover all three possible outcomes:</p>
<ul>
<li>the worker method completes before the timeout expires,</li>
<li>the timeout expires before the worker method reaches the point of no return, so the method aborts,</li>
<li>the timeout expires after the worker method reaches the point of no return, so the method continues in the background.</li>
</ul>
<p>Check <a href="20261009-RefactorTestsToMakeThemMoreReadable.html">my next blog post</a> how I refactored these tests to make them easier to understand.</p>
<p>You might wonder why I haven&#39;t used this approach in my old blog post. For some reason, I have only recently learned about the request timeouts middleware although it has been available since .NET 8. That&#39;s also why I decided to now revisit the topic once more.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20261002-UsingRequestTimeoutsMiddlewareInWebApi.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20261002-UsingRequestTimeoutsMiddlewareInWebApi.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Model variable JSON field types with unions]]></title>
            <description><![CDATA[<p>A while back I wrote <a href="20241011-ParsingJsonWithVariableFieldTypes.html">a blog post about deserializing a JSON with a field of a variable type</a> in .NET. At that time I used a custom serializer to achieve that. This scenario seems like a good fit for <a href="20260821-UnionTypesInCs15.html">unions which are being added in C# 15</a>. That&#39;s a good reason to revisit the topic.</p>
<p>I was dealing with a field that could be serialized into two different JSON types, depending on its contents. It could be serialized as string:</p>
<pre class="highlight"><code class="hljs json">{
  "<span class="hljs-attribute">Version</span>": <span class="hljs-value"><span class="hljs-string">"1.0.0"</span>
</span>}
</code></pre>
<p>Or, it could be serialized as a number:</p>
<pre class="highlight"><code class="hljs json">{
  "<span class="hljs-attribute">Version</span>": <span class="hljs-value"><span class="hljs-number">1</span>
</span>}
</code></pre>
<p>A union type is dedicated to modeling data which can be represented as one of multiple types, in this case a string or a number. And if you need to always access it as a string as I did, you can add custom property for that:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> union <span class="hljs-title">StringOrNumber</span><span class="hljs-params">(<span class="hljs-keyword">string</span>, <span class="hljs-keyword">long</span>)</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? StringValue =&gt; <span class="hljs-keyword">this</span> <span class="hljs-keyword">switch</span>
    {
        <span class="hljs-keyword">string</span> stringValue =&gt; stringValue,
        <span class="hljs-keyword">long</span> longValue =&gt; longValue.ToString(),
        <span class="hljs-keyword">null</span> =&gt; <span class="hljs-keyword">null</span>
    };
}
</code></pre>
<p>You can now use this type to model the multi-type field in the JSON object:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AppInfo</span>
{
    <span class="hljs-keyword">public</span> StringOrNumber Version { <span class="hljs-keyword">get</span>; init; }
}
</code></pre>
<p>With this setup, the deserialized value is typed correctly for both of the above cases, i.e., string or number, respectively. And the <code>StringValue</code> returns its string equivalent:</p>
<pre class="highlight"><code class="hljs csharp">[Test]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DeserializesStringValue</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> json = <span class="hljs-string">""</span><span class="hljs-string">"{"</span>Version<span class="hljs-string">":"</span><span class="hljs-number">1.0</span><span class="hljs-number">.0</span><span class="hljs-string">"}"</span><span class="hljs-string">""</span>;

    <span class="hljs-keyword">var</span> appInfo = JsonSerializer.Deserialize&lt;AppInfo&gt;(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo(<span class="hljs-string">"1.0.0"</span>));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo(<span class="hljs-string">"1.0.0"</span>));
}

[Test]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DeserializesNumericValue</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> json = <span class="hljs-string">""</span><span class="hljs-string">"{"</span>Version<span class="hljs-string">":1}"</span><span class="hljs-string">""</span>;

    <span class="hljs-keyword">var</span> appInfo = JsonSerializer.Deserialize&lt;AppInfo&gt;(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo(<span class="hljs-number">1</span>));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo(<span class="hljs-string">"1"</span>));
}
</code></pre>
<p>And if the <code>Version</code> field is missing or set to <code>null</code>, the typed and string value are also both <code>null</code>. Only the union type is non-<code>null</code> since it&#39;s a value type:</p>
<pre class="highlight"><code class="hljs csharp">[Test]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">DeserializesMissingValue</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> json = <span class="hljs-string">""</span><span class="hljs-string">"{"</span>Version<span class="hljs-string">":null}"</span><span class="hljs-string">""</span>;

    <span class="hljs-keyword">var</span> appInfo = JsonSerializer.Deserialize&lt;AppInfo&gt;(json);

    Assert.That(appInfo, Is.Not.Null);
    Assert.That(appInfo.Version.Value, Is.EqualTo(<span class="hljs-keyword">null</span>));
    Assert.That(appInfo.Version.StringValue, Is.EqualTo(<span class="hljs-keyword">null</span>));
}
</code></pre>
<p>You can find a sample project with all code from this blog post in <a href="https://github.com/DamirsCorner/20260925-dotnet-json-union-types">my GitHub repository</a>. It includes the tests for serialization as well. It works as expected without any additional code.</p>
<p>Union types are a useful addition to C#. Some scenarios which required special handling in the past now just work out of the box. To try them out, you will need the latest .NET 11 SDK pre-release, Release Candidate 1 at the time of writing. It comes even with <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-rc-1/">a go-live support license</a>, so you can deploy it to production if you feel courageous. I would suggest you wait at least until the final release in November, though.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260925-ModelVariableJsonFieldTypesWithUnions.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260925-ModelVariableJsonFieldTypesWithUnions.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 25 Sep 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Labeled break and continue statements in C# 15]]></title>
            <description><![CDATA[<p><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/jump-statements#the-goto-statement">Goto statements</a> have been a part of C# since the very first version, but at least I haven&#39;t seen them being used very often. In C# 15, labels can also be used in combination with continue and break statements. What are the use cases and how often can we expect them being used?</p>
<p><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/labeled-break-continue">Labeled break and continue statements</a> are useful exclusively in nested loops to make exiting from inner loops simpler and more readable. Here&#39;s a small example taking advantage of the new syntax:</p>
<pre class="highlight"><code class="hljs csharp">outer: <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> row = <span class="hljs-number">0</span>; row &lt; values.GetLength(<span class="hljs-number">0</span>); row++)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> col = <span class="hljs-number">0</span>; col &lt; values.GetLength(<span class="hljs-number">1</span>); col++)
    {
        cellsProcessed++;
        <span class="hljs-keyword">if</span> (shouldContinue(values[row,col]))
            <span class="hljs-keyword">continue</span> outer;
        <span class="hljs-keyword">if</span> (shouldBreak(values[row, col]))
            <span class="hljs-keyword">break</span> outer;
    }
    rowsProcessed++;
}
</code></pre>
<p>Notice the <code>outer</code> label on the outer <code>for</code> loop. It&#39;s being referenced from the <code>continue</code> and <code>break</code> statements inside the inner <code>for</code> loop. Without the labels the two statements would affect the inner loop. With the labels, they affect the outer loop.</p>
<p>Before C# 15, equivalent code could best be written using <code>goto</code> statements:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> row = <span class="hljs-number">0</span>; row &lt; values.GetLength(<span class="hljs-number">0</span>); row++)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> col = <span class="hljs-number">0</span>; col &lt; values.GetLength(<span class="hljs-number">1</span>); col++)
    {
        cellsProcessed++;
        <span class="hljs-keyword">if</span> (shouldContinue(values[row,col]))
            <span class="hljs-keyword">goto</span> outer_continue;
        <span class="hljs-keyword">if</span> (shouldBreak(values[row, col]))
            <span class="hljs-keyword">goto</span> outer_break;
    }
    rowsProcessed++;
    outer_continue: ;
}

outer_break: ;
</code></pre>
<p>The code isn&#39;t much different but its semantics aren&#39;t as obvious. The two labels (<code>outer_continue</code> and <code>outer_break</code>) mark the spot to jump to instead of the loop being affected. And the <code>goto</code> statement intention is less clear than the intention of <code>continue</code> and <code>break</code> statements. Naming the labels appropriately helps somewhat, though.</p>
<p>If you wanted to avoid using <code>goto</code> statements because they are <a href="https://en.wikipedia.org/wiki/Considered_harmful">considered harmful</a>, the code would get even more convoluted:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> row = <span class="hljs-number">0</span>; row &lt; values.GetLength(<span class="hljs-number">0</span>); row++)
{
    <span class="hljs-keyword">var</span> continueFlag = <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> col = <span class="hljs-number">0</span>; col &lt; values.GetLength(<span class="hljs-number">1</span>); col++)
    {
        cellsProcessed++;
        <span class="hljs-keyword">if</span> (shouldContinue(values[row, col]))
        {
            continueFlag = <span class="hljs-keyword">true</span>;
            <span class="hljs-keyword">break</span>;
        }
        <span class="hljs-keyword">if</span> (shouldBreak(values[row, col]))
        {
            breakFlag = <span class="hljs-keyword">true</span>;
            <span class="hljs-keyword">break</span>;
        }
    }

    <span class="hljs-keyword">if</span> (breakFlag)
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">if</span> (!continueFlag)
        rowsProcessed++;
}
</code></pre>
<p>Admittedly, this is a direct rewrite of the original code from above without labels. It could probably be somewhat improved with further refactoring but the intention of the code still wouldn&#39;t be as clear as in the version with labeled <code>continue</code> and <code>break</code> statements.</p>
<p>As all C# 15 language features, labeled continue and break statements are still in preview and could change before C# 15 is released. With <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/">.NET 11 preview 7</a>, you have to set the language version to preview in your project to use them:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>I have created a sample project with the three code snippets above slightly expanded and covered with tests. You can clone it from my <a href="https://github.com/DamirsCorner/20260918-cs15-labeled-break-continue">GitHub repository</a>.</p>
<p>I don&#39;t think labeled continue and break statements will be used much, just like goto statements don&#39;t seem to be. However, in the very specific scenarios they are addressing, using them is worth it as it can make code easier to write and understand.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260918-LabeledBreakAndContinueStatementsInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260918-LabeledBreakAndContinueStatementsInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Indexer extension members in C# 15]]></title>
            <description><![CDATA[<p>Last year, <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods">extension members</a> were the biggest new feature of C# 14. I wrote about them in detail <a href="20250829-ExtensionMembersInCs14.html">in</a> <a href="20250905-GenericExtensionMembersInCs14.html">multiple</a> <a href="20250912-DisambiguationOfExtensionMembersInCs14.html">blogposts</a>. This year, <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/extension-indexers">extension indexers</a> are being added to C# 15 which didn&#39;t make it into C# 14.</p>
<p>Extension methods have been added to C# as a part of LINQ a long time ago. In essence, they were static methods which could be called as if they were members of the type of their first parameter:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span>? FirstCharToUpper(<span class="hljs-keyword">this</span> <span class="hljs-keyword">string</span>? receiver)
{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.IsNullOrEmpty(receiver)
        ? receiver
        : <span class="hljs-keyword">string</span>.Concat(receiver[.<span class="hljs-number">.1</span>].ToUpper(), receiver.AsSpan(<span class="hljs-number">1</span>));
}
</code></pre>
<p>They had to be placed in a static class and the first parameter had to be introduced with <code>this</code> keyword. Such a method could be called as a member method of its first parameter which therefore didn&#39;t have to be passed in as a parameter:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-string">"me"</span>.FirstCharToUpper()
</code></pre>
<p>The extension members feature of C# 14 added support for adding other member types as extensions. To make this possible, the syntax for declaring them had to be different from the existing extension method syntax. Their syntax is more similar to regular type members, but they have to be inside an <code>extension</code> block, in a static class. The old syntax is still supported for extension instance methods, but a new one was added to match the syntax of other extension member types:</p>
<pre class="highlight"><code class="hljs csharp">extension(<span class="hljs-keyword">string</span>? receiver)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? FirstCharToUpper()
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.IsNullOrEmpty(receiver)
            ? receiver
            : <span class="hljs-keyword">string</span>.Concat(receiver[.<span class="hljs-number">.1</span>].ToUpper(), receiver.AsSpan(<span class="hljs-number">1</span>));
    }
}
</code></pre>
<p>Other supported member types were:</p>
<ul>
<li>instance properties:<pre class="highlight"><code class="hljs csharp">extension(<span class="hljs-keyword">string</span>? receiver)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> IsEmptyField =&gt;
        <span class="hljs-keyword">string</span>.IsNullOrEmpty(receiver) || receiver == <span class="hljs-string">"N/A"</span>;
}
</code></pre>
</li>
<li><p>static methods:</p>
<pre class="highlight"><code class="hljs csharp">extension(<span class="hljs-keyword">string</span>?)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span>? Create(<span class="hljs-keyword">string</span>? pattern, <span class="hljs-keyword">int</span> count)
    {
        <span class="hljs-keyword">if</span> (pattern == <span class="hljs-keyword">null</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }

        <span class="hljs-keyword">if</span> (pattern.Length == <span class="hljs-number">0</span> || count &lt;= <span class="hljs-number">0</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.Empty;
        }

        <span class="hljs-keyword">var</span> builder = <span class="hljs-keyword">new</span> StringBuilder(pattern.Length * count);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; count; i++)
        {
            builder.Append(pattern);
        }

        <span class="hljs-keyword">return</span> builder.ToString();
    }
}
</code></pre>
</li>
<li><p>static properties:</p>
<pre class="highlight"><code class="hljs csharp">extension(<span class="hljs-keyword">string</span>?)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> NotAvailable =&gt; <span class="hljs-string">"N/A"</span>;
}
</code></pre>
</li>
<li>operators:<pre class="highlight"><code class="hljs csharp">extension(<span class="hljs-keyword">string</span>?)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span>? <span class="hljs-keyword">operator</span> *(<span class="hljs-keyword">string</span>? pattern, <span class="hljs-keyword">int</span> count)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.Create(pattern, count);
    }
}
</code></pre>
</li>
</ul>
<p>In addition to all these extension member types, C# 15 now also supports extension indexers. The syntax follows the pattern of all other extension members:</p>
<pre class="highlight"><code class="hljs csharp">extension(IEnumerable&lt;<span class="hljs-keyword">int</span>&gt; sequence)
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-keyword">this</span>[<span class="hljs-keyword">int</span> index] =&gt; sequence.ElementAt(index);
}
</code></pre>
<p>Just like other extension member types, extension indexers are only considered by the compiler when there are no matching actual member indexers. Even <a href="https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/ranges-indexes#type-support-for-indices-and-ranges">implicit <code>Index</code> and <code>Range</code> indexers</a> have precedence over extension indexers with the same type. E.g., if a type has an <code>int</code> indexer and a <code>Length</code> or <code>Count</code> property, then the <code>Index</code> indexer is implicitly supported and an extension <code>Index</code> indexer will be ignored even if you implement it.</p>
<p>In addition to all that, extension indexers for arrays and strings will be ignored altogether as documented at the very bottom in the collapsed section of <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-15.0/extension-indexers#open-issues">the feature specification</a>:</p>
<blockquote>
<p>The current spec and baseline rules for <a href="https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#128122-array-access">array access</a> and <a href="https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#128123-string-access">string access</a> mean that extension indexers don&#39;t work on arrays or strings.<br>Yet the declaration of such extension indexers is permitted.</p>
</blockquote>
<p>Extension indexers are currently a preview feature and might still change. To use them in <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/">.NET 11 preview 7</a>, you must set the language version to preview in your project:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>A sample project with all supported extension member types (the ones from C# 14 and the indexers newly added in C# 15) is available in my <a href="https://github.com/DamirsCorner/20260911-cs15-extension-indexers">GitHub repository</a>. Feel free to clone it and try out the feature yourself.</p>
<p>With the addition of extension indexers in C# 15, the originally planned scope of extension members from C# 14 has been completed. The only remaining member type without extension support are conversion operators which aren&#39;t even planned for now as stated in <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-14.0/extension-operators">the feature specification</a>:</p>
<blockquote>
<p>Does not cover user-defined implicit and explicit conversion operators, which are not yet designed or planned.</p>
</blockquote>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260911-IndexerExtensionMembersInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260911-IndexerExtensionMembersInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 11 Sep 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Collection expressions with arguments in C# 15]]></title>
            <description><![CDATA[<p><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions">Collection expressions</a> were introduced in C# 12. I wrote <a href="20231020-BenefitsOfCollectionExpressions.html">a blog post</a> about them at that time. In C# 15, the syntax is being extended with the ability to provide additional arguments to the underlying collection constructor or builder.</p>
<p>Let&#39;s start with a quick reminder, what collection expressions are. Since the early days of C#, collection types could be initialized using the collection initializer syntax. For example, an array could be initialized with:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> oldSyntax = <span class="hljs-keyword">new</span>[] { <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span> };
</code></pre>
<p>Collection expressions provide an alternative syntax (notice how the variable has to be explicitly typed so that the compiler knows which type of collection to instantiate):</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">int</span>[] newSyntax = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
</code></pre>
<p>The new syntax also added support for the spread element to initialize a new collection as a concatenation of elements from multiple existing collections, inspired by the Javascript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">spread syntax</a>:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">int</span>[] array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>];
Span&lt;<span class="hljs-keyword">int</span>&gt; span = [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
List&lt;<span class="hljs-keyword">int</span>&gt; list = [<span class="hljs-number">5</span>, <span class="hljs-number">6</span>];
<span class="hljs-keyword">int</span>[] finalArray = [.. array, .. span, .. list];
</code></pre>
<p>However, the new collection expression wasn&#39;t a full replacement for all use cases. Some collection constructors have parameters which tweak how the collection gets created. For example, a list can be created with a larger initial size, which is useful if we know in advance that more items will be added to it:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> oldSyntax = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">int</span>&gt;(<span class="hljs-number">10</span>) { <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span> };
</code></pre>
<p>Before C# 15, the same final result couldn&#39;t be achieved using collection expression syntax. The addition of so-called <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions#collection-expression-arguments">collection expression arguments</a> in C# 15 makes it possible to specify values for the collection constructor parameters. The following syntax is equivalent to the one above:</p>
<pre class="highlight"><code class="hljs csharp">List&lt;<span class="hljs-keyword">int</span>&gt; newSyntax = [with(<span class="hljs-number">10</span>), <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
</code></pre>
<p>The constructor parameters are specified using the <code>with</code> keyword which has to be the first element in the collection expression. Its parameters must match the parameters of a constructor of the collection type being initialized.</p>
<p>Collection initializers can&#39;t be used with all collections. <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable">Immutable collections</a> are such an example. The closest alternative without using collection expressions are the builder methods with <code>params</code> arguments. This means that the following call invokes the <code>Create&lt;string&gt;(params ReadOnlySpan&lt;string&gt; items)</code> method (and called <code>Create&lt;string&gt;(params string[] items)</code> before <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#15624-parameter-collections">parameter collections</a> from C# 13 added <code>params</code> support to other types of collections):</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> oldSyntax = ImmutableHashSet.Create(<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>);
</code></pre>
<p>Collection expression can invoke the same method using the syntax identical to other types of collections which support collection initializers:</p>
<pre class="highlight"><code class="hljs csharp">ImmutableHashSet&lt;<span class="hljs-keyword">string</span>&gt; newSyntax = [<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>];
</code></pre>
<p>With the addition of collection expression arguments in C# 15, collection builders with other arguments in addition to the span or array for specifying the elements to be added to the collection can now supported by the collection expression syntax. For example:</p>
<pre class="highlight"><code class="hljs csharp">ImmutableHashSet&lt;<span class="hljs-keyword">string</span>&gt; newSyntax =
[
  with(StringComparer.OrdinalIgnoreCase),
  <span class="hljs-string">"a"</span>,
  <span class="hljs-string">"A"</span>
];
</code></pre>
<p>This new syntax is equivalent to the old one without collection expressions:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> oldSyntax = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, <span class="hljs-string">"a"</span>, <span class="hljs-string">"A"</span>);
</code></pre>
<p>As you can deduce from the given example, in the case of builder methods the collection expression arguments map to the builder method parameters placed before the span <code>items</code> parameter.</p>
<p>Just like all other C# 15 features, collection expression arguments are still in preview and might change before the November release. To use them in <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/">.NET 11 preview 7</a> you have to set the language version to preview:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>I created a sample project with all the code from this blog post and pushed it to <a href="https://github.com/DamirsCorner/20260904-cs15-collection-expression-arguments">my GitHub repository</a>. It&#39;s an easy way to try out this feature yourself.</p>
<p>Collection expression arguments are in essence only syntactic sugar. But so were collection expressions themselves and I still see them used quite regularly nowadays. WIth this new addition, collection expressions can now be used in a few more cases where they couldn&#39;t be before.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260904-CollectionExpressionsWithArgumentsInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260904-CollectionExpressionsWithArgumentsInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 04 Sep 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Custom union types in C# 15]]></title>
            <description><![CDATA[<p>In <a href="20260821-UnionTypesInCs15.html">the previous blog post</a> I explored what union types bring to C#. But what does the compiler do under the hood? And do I need to know about that to fully take advantage of union types?</p>
<p>Each union type implements the <code>IUnion</code> interface with a single <code>Value</code> property:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IUnion</span>
{
    <span class="hljs-keyword">object</span>? Value { <span class="hljs-keyword">get</span>; }
}
</code></pre>
<p>To initialize the <code>Value</code> property, a constructor is generated for each case type, e.g.:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Shape</span><span class="hljs-params">(Square square)</span>
</span>{
    Value = square;
}
</code></pre>
<p>This constructor is used by the implicit conversion operator which allows us to directly assign an instance of any case type to the union type:</p>
<pre class="highlight"><code class="hljs csharp">Shape shape = <span class="hljs-keyword">new</span> Square(<span class="hljs-number">2</span>);
</code></pre>
<p>Since the union type is just a regular type, we can add custom members to it:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> union <span class="hljs-title">Shape</span><span class="hljs-params">(Square, Circle)</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> Area =&gt;
        <span class="hljs-keyword">this</span> <span class="hljs-keyword">switch</span>
        {
            Square square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
            Circle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>)
        };
}
</code></pre>
<p>By following the requirements for a union type we can even implement our own custom union type which supports exhaustive <code>switch</code> expressions just like the autogenerated one. It has to:</p>
<ul>
<li>implement the <code>IUnion</code> interface</li>
<li>have a constructor for each case type with its instance as the only parameter</li>
<li>return that case type instance via the <code>Value</code> property</li>
<li>be annotated with the <code>[Union]</code> attribute</li>
</ul>
<pre class="highlight"><code class="hljs csharp">[Union]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">struct</span> CustomShape : IUnion
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">object</span>? Value { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomShape</span><span class="hljs-params">(Square square)</span>
    </span>{
        Value = square;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CustomShape</span><span class="hljs-params">(Circle circle)</span>
    </span>{
        Value = circle;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomShape</span><span class="hljs-params">(Square square)</span> </span>=&gt; <span class="hljs-keyword">new</span>(square);

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">implicit</span> <span class="hljs-keyword">operator</span> <span class="hljs-title">CustomShape</span><span class="hljs-params">(Circle circle)</span> </span>=&gt; <span class="hljs-keyword">new</span>(circle);
}
</code></pre>
<p>The implicit conversion operators are optional. If we don&#39;t implement them, the compiler will generate them for us.</p>
<p>But why would we even want to implement our own custom union type when the compiler can do it for us? It could be for performance reasons. The compiler-generated union type always stores the case type instance as an <code>object</code> just like the struct above. If our case types are value types, this means that they are boxed every time they are stored in the union type and unboxed every time they are read from it. We can avoid this with a custom union type:</p>
<pre class="highlight"><code class="hljs csharp">[Union]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">struct</span> IntOrDouble : IUnion
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">enum</span> ValueType : <span class="hljs-keyword">byte</span>
    {
        None,
        Int,
        Double,
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span> _intValue;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">double</span> _doubleValue;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ValueType _type = ValueType.None;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">object</span>? Value =&gt;
        _type <span class="hljs-keyword">switch</span>
        {
            ValueType.Int =&gt; _intValue,
            ValueType.Double =&gt; _doubleValue,
            _ =&gt; <span class="hljs-keyword">null</span>,
        };

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntOrDouble</span><span class="hljs-params">(<span class="hljs-keyword">int</span> <span class="hljs-keyword">value</span>)</span>
    </span>{
        _type = ValueType.Int;
        _intValue = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntOrDouble</span><span class="hljs-params">(<span class="hljs-keyword">double</span> <span class="hljs-keyword">value</span>)</span>
    </span>{
        _type = ValueType.Double;
        _doubleValue = <span class="hljs-keyword">value</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">TryGetValue</span><span class="hljs-params">(<span class="hljs-keyword">out</span> <span class="hljs-keyword">int</span> <span class="hljs-keyword">value</span>)</span>
    </span>{
        <span class="hljs-keyword">value</span> = _intValue;
        <span class="hljs-keyword">return</span> _type == ValueType.Int;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">TryGetValue</span><span class="hljs-params">(<span class="hljs-keyword">out</span> <span class="hljs-keyword">double</span> <span class="hljs-keyword">value</span>)</span>
    </span>{
        <span class="hljs-keyword">value</span> = _doubleValue;
        <span class="hljs-keyword">return</span> _type == ValueType.Double;
    }
}
</code></pre>
<p>Notice the two <code>TryGetValue</code> methods in this custom union type. When they are present, the compiler uses them in favor of the <code>Value</code> property when the union type is used with type matching, e.g.,</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">if</span> (union <span class="hljs-keyword">is</span> <span class="hljs-keyword">int</span> <span class="hljs-keyword">value</span>)
{
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>These methods make sure that boxing and unboxing can be completely avoided. Of course, if the <code>Value</code> property is accessed directly, the value will still be boxed, as there is no other way to return a value type as an <code>object</code>.</p>
<p>As already mentioned in the previous blog post, union types are still in preview and can change before the final release, but they are already available to try out in <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/">.NET 11 preview 7</a>. You have to set the language version to preview in your project for them to work:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>You can find a sample project with all the code from this blog post and more in <a href="https://github.com/DamirsCorner/20260828-cs15-custom-union-types">my GitHub repository</a>. Feel free to clone it and play around with it.</p>
<p>Although you can use union types as soon as you understand their basic syntax, it&#39;s interesting to learn what the compiler does for you. And while it&#39;s unlikely that you&#39;ll need to implement your own custom union type, it&#39;s good to know that it&#39;s possible.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260828-CustomUnionTypesInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260828-CustomUnionTypesInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 28 Aug 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Union types in C# 15]]></title>
            <description><![CDATA[<p>Continuing with the topic of <code>switch</code> expression exhaustiveness in C# 15, I chose <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-15#union-types">union types</a> as the next new language feature to explore.</p>
<p>Before C# 15, there were several ways to return multiple different types from a method or store them in a single variable. You can use the type of the common ancestor of your types. This gives you two options for your types</p>
<ol>
<li><p>They have to implement the same interface. If the only purpose of the interface is to provide a common ancestry (i.e., it doesn&#39;t define any members to implement), such an interface is called a marker interface:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IShape</span> { }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Square</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span> : IShape</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span> : IShape</span>;
</code></pre>
</li>
<li><p>They have to derive from the same base class (usually abstract if it&#39;s only used to provide common ancestry):</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> record Shape { }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Square</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span> : Shape</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span> : Shape</span>;
</code></pre>
</li>
</ol>
<p>In both cases, a catch-all <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#discard-pattern">discard pattern</a> has to be added to <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression">a type-based <code>switch</code> expression</a>:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">return</span> shape <span class="hljs-keyword">switch</span>
{
    Square square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    Circle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>),
    _ =&gt; <span class="hljs-function"><span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title">NotSupportedException</span><span class="hljs-params">($<span class="hljs-string">"{shape.GetType()} is not supported."</span>)</span>,
}</span>;
</code></pre>
<p>Otherwise, the following warning is generated by the compiler:</p>
<blockquote>
<p>Warning CS8509 : The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern <code>_</code> is not covered.</p>
</blockquote>
<p>Unfortunately that same discard pattern which resolves the warning also means that the compiler can&#39;t generate a warning if you add another sibling type at a later time. That type will already be covered by the discard pattern as far as the compiler is concerned.</p>
<p>For the abstract base class approach, C# 15 changes this with the introduction of closed class hierarchies which I explored in <a href="20260814-ClosedClassHierarchiesInCs15.html">my previous blog post</a>.</p>
<p>But what if you can&#39;t derive your types from a common ancestor (e.g., because you are using types which you haven&#39;t defined yourself)?</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Square</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span></span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span></span>;
</code></pre>
<p>The only obvious choice before C# 15 was to use <code>object</code> as the return type or variable type. Since the <code>object</code> type doesn&#39;t constraint the type of the assigned value in any way, the compiler of course can&#39;t reason about the exhaustiveness of a <code>switch</code> expression and always requires a discard pattern to be added.</p>
<p><a href="https://github.com/mcintyre321/OneOf">The <code>OneOf</code> library</a> provides one possible solution for this challenge. It introduces a generic <code>OneOf</code> type which you can use to combine multiple unrelated types into one. You can even give a custom name to that type by taking advantage of the using alias directives:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">using</span> Shape = OneOf&lt;Square, Circle&gt;;
</code></pre>
<p>This doesn&#39;t change how <code>switch</code> expressions treat these type in any way. However, the <code>OneOf</code> type comes with a <code>Match</code> method which you can use instead:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">return</span> shape.Match(
    square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>)
);
</code></pre>
<p>Although the syntax is different, the functionality is equivalent. But if you add another type to your <code>OneOf</code> type alias:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">using</span> Shape = OneOf&lt;Square, Circle, Rectangle&gt;;
</code></pre>
<p>The code won&#39;t build anymore:</p>
<blockquote>
<p>Error CS7036 : There is no argument given that corresponds to the required parameter <code>f2</code> of <code>OneOf&lt;Square, Circle, Rectangle&gt;.Match&lt;TResult&gt;(Func&lt;Square, TResult&gt;, Func&lt;Circle, TResult&gt;, Func&lt;Rectangle, TResult&gt;)</code></p>
</blockquote>
<p>The number of parameters of the <code>Match</code> method matches the number of generic type arguments in your <code>OneOf</code> type declaration. Therefore, when you add another type argument, you must also add another method parameter or the code won&#39;t compile. Your code won&#39;t behave incorrectly because you forgot to do that.</p>
<p>With C# 15 you have an alternative to the <code>OneOf</code> type which is built into the language: union types. Just like the <code>OneOf</code> type, it allows you to specify which (unrelated) types you want to work with:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> union <span class="hljs-title">Shape</span><span class="hljs-params">(Square, Circle)</span></span>;
</code></pre>
<p>Unlike the <code>OneOf</code> type, you can now use these types in a <code>switch</code> expression without any warnings even if you don&#39;t include a discard pattern:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">return</span> shape <span class="hljs-keyword">switch</span>
{
    Square square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    Circle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>)
};
</code></pre>
<p>And when you add another type to your union:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> union <span class="hljs-title">Shape</span><span class="hljs-params">(Square, Circle, Rectangle/)</span></span>;
</code></pre>
<p>The compiler will generate a warning if you don&#39;t handle that new type in the <code>switch</code> expression:</p>
<blockquote>
<p>Warning CS8509 : The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern <code>Cs15UnionTypes.Union.Rectangle</code> is not covered.</p>
</blockquote>
<p>It&#39;s the same behavior as when using closed class hierarchies.</p>
<p>Of course, the feature is still in preview and it can change before the final release. You can already try it out in its current state with <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-7/">.NET 11 preview 7</a>. To enable it, you have to set the language version to preview in your project:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>I created a sample project with all five approaches from this post and put it in <a href="https://github.com/DamirsCorner/20260821-cs15-union-types">my GitHub repository</a>. You can use it as a starting point to explore this feature on your own.</p>
<p>Union types are a complement to closed class hierarchies in C# 15. They both improve the experience when using type-based <code>switch</code> expressions, each for its own use case. Where closed class hierarchies address exhaustiveness for types with a common base type, unions do it for unrelated types. And give you a way to declare a predefined set of unrelated types to return from a method or assign to a variable or parameter.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260821-UnionTypesInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260821-UnionTypesInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 21 Aug 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Closed class hierarchies in C# 15]]></title>
            <description><![CDATA[<p>The release of C# 15 is getting closer and its new features started to show up in the .NET 11 previews. That&#39;s a good time for me to take a closer look at what to expect. I&#39;m starting with <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-15#closed-hierarchies">closed class hierarchies</a>.</p>
<p>Class hierarchies are referring to a base class and the classes derived from it. For example:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> record OpenShape { }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">OpenSquare</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span> : OpenShape </span>{ }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">OpenCircle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span> : OpenShape </span>{ }
</code></pre>
<p><a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression">A <code>switch</code> expression</a> can be used to handle each type differently:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> area = shape <span class="hljs-keyword">switch</span>
{
    OpenSquare square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    OpenCircle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>),
};
</code></pre>
<p>Although the code covers all the classes, currently derived from <code>OpenShape</code>, the code above still generates a compiler warning:</p>
<blockquote>
<p>Warning CS8509 : The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern <code>_</code> is not covered.</p>
</blockquote>
<p>That&#39;s because the compiler cannot be sure that the code won&#39;t encounter additional classes derived from <code>OpenShape</code> at runtime. Such a class could be defined in a different assembly and passed into code from the original assembly which was compiled without the knowledge of this new class. Such a class hierarchy is called an open class hierarchy because it&#39;s open for adding new classes to it. To fix the warning, a catch-all <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#discard-pattern">discard pattern</a> has to be added to the <code>switch</code> expression which will handle any classes that are not handled explicitly:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> area = shape <span class="hljs-keyword">switch</span>
{
    OpenSquare square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    OpenCircle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>),
    _ =&gt; <span class="hljs-function"><span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title">NotSupportedException</span><span class="hljs-params">($<span class="hljs-string">"{shape.GetType()} is not supported."</span>)</span>,
}</span>;
</code></pre>
<p>Even before C# 15, there is a way to prevent new classes from being derived from the base class in a different assembly, with access modifiers making the base class constructor inaccessible from other assemblies:</p>
<ul>
<li>Use <code>internal</code>, if you want the constructor publicly accessible in the original assembly.</li>
<li>Use <code>private protected</code>, if you want the constructor to be protected in the original assembly, i.e., only accessible from derived classes.</li>
</ul>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> record PrivateShape
{
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">protected</span> <span class="hljs-title">PrivateShape</span><span class="hljs-params">()</span> </span>{ }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">PrivateSquare</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span> : PrivateShape </span>{ }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">PrivateCircle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span> : PrivateShape </span>{ }
</code></pre>
<p>Trying to derive from <code>PrivateShape</code> in a different assembly will generate the following error:</p>
<blockquote>
<p>Error CS7036 : There is no argument given that corresponds to the required parameter &#39;original&#39; of &#39;PrivateShape.PrivateShape(PrivateShape)&#39;</p>
</blockquote>
<p>This is caused by the fact that there is now no parameterless constructor accessible. The constructor mentioned in the error message is the <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#nondestructive-mutation">automatically generated copy constructor for a record</a>.</p>
<p>Although this effectively ensures that no class can be derived from <code>PrivateShape</code> in a different assembly, the compiler will still generate a warning for a <code>switch</code> expression without the discard pattern.</p>
<p>However, C# 15 introduces a new keyword <code>closed</code> which can be used on a base class to disallow deriving from it in a different assembly:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">public</span> closed record ClosedShape { }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ClosedSquare</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length)</span> : ClosedShape </span>{ }

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ClosedCircle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Radius)</span> : ClosedShape </span>{ }
</code></pre>
<p>When trying to do so, the following error will be generated:</p>
<blockquote>
<p>Error CS9382 : &#39;ClosedRectangle&#39;: cannot use a closed type &#39;ClosedShape&#39; from another assembly as a base type.</p>
</blockquote>
<p>A class hierarchy with a <code>closed</code> class as its base is treated by the compiler as a closed class hierarchy, meaning that it is closed for adding new classes. A <code>switch</code> expression over such a class hierarchy doesn&#39;t require a discard catch-all pattern. The following block of code will compile without warnings:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-keyword">var</span> area = shape <span class="hljs-keyword">switch</span>
{
    ClosedSquare square =&gt; Math.Pow(square.Length, <span class="hljs-number">2</span>),
    ClosedCircle circle =&gt; Math.PI * Math.Pow(circle.Radius, <span class="hljs-number">2</span>),
};
</code></pre>
<p>You might wonder what is the big deal about having or not having to add a discard pattern to a switch expression. After all, it&#39;s only a single line of code. But this line of code has an important impact on what will happen when you add another class to the hierarchy, e.g.:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ClosedRectangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> Length, <span class="hljs-keyword">double</span> Width)</span> : ClosedShape </span>{ }
</code></pre>
<p>If there is a discard pattern in the <code>switch</code> expression, no new warnings will be generated when you add such a class. Only at run time will you notice that an exception is thrown because you forgot to handle the new class. However, without the discard pattern, a warning will be generated when you add a class to the hierarchy, notifying you immediately that you need to properly handle this new class:</p>
<blockquote>
<p>Warning CS8509 : The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern &#39;ClosedClassHierarchies.CoreLib.ClosedRectangle&#39; is not covered.</p>
</blockquote>
<p>You might also argue that you can still extend a closed class hierarchy indirectly by deriving from a derived class instead of the base class:</p>
<pre class="highlight"><code class="hljs csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ClosedColoredSquare</span><span class="hljs-params">(Color Color, <span class="hljs-keyword">double</span> Length)</span>
    : <span class="hljs-title">ClosedSquare</span><span class="hljs-params">(Length)</span> </span>{ }
</code></pre>
<p>Such a class doesn&#39;t affect the exhaustiveness of a <code>switch</code> expression, though. The new class will be handled by the existing <code>ClosedSquare</code> case.</p>
<p>As already mentioned, the feature is still in preview, so it can change before the final release. You can already try it out in its current state with <a href="https://devblogs.microsoft.com/dotnet/dotnet-11-preview-6/">.NET 11 preview 6</a>. To enable it, you have to set the language version to preview in your project:</p>
<pre class="highlight"><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-title">PropertyGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-title">LangVersion</span>&gt;</span>preview<span class="hljs-tag">&lt;/<span class="hljs-title">LangVersion</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-title">PropertyGroup</span>&gt;</span>
</code></pre>
<p>You can find a small sample with all the code from this article and more in <a href="https://github.com/DamirsCorner/20260814-cs15-closed-class-hierarchies">my GitHub repository</a>. Feel free to clone it and use it as a basis for experimenting with this new feature yourself.</p>
<p>The closed class hierarchies with the <code>closed</code> keywords make the type-based <code>switch</code> expressions safer to use. As long as you don&#39;t ignore the warnings in your project you should never again forget to modify an existing <code>switch</code> expression when extending a class hierarchy with a new class.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260814-ClosedClassHierarchiesInCs15.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260814-ClosedClassHierarchiesInCs15.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Switching restic backup from NFS to SFTP]]></title>
            <description><![CDATA[<p><a href="20250711-BackingUpLinuxHomeServerUsingRestic.html">For over a year</a>, I&#39;ve been using restic to back up my home lab server to a repository on a Synology NAS mounted locally using NFS. All this time, it worked for me without any issues whatsoever. Only when I configured the repository the same way on my new Linux-based home computer, I noticed that something was off.</p>
<p>The problem was that I could only access the repository with root privileges. That was a given on my home lab server where the backups where always created by a scheduled script running in root context. On my home computer, I want to interact with the restic repository as myself without having to elevate privileges.</p>
<p>As I investigated further, I noticed that the files created in the repository were owned by an unknown user, i.e., a user with UID 1000 which didn&#39;t even exist on Synology. It turned out that it was the UID of my home lab server user. I haven&#39;t encountered any issues before only because the NFS mount was always used as root on my home server and therefore permissions hadn&#39;t been checked.</p>
<p>I started playing around with NFS security settings on Synology but I couldn&#39;t find a way to interact with the repository as my Synology user. As stated in <a href="https://kb.synology.com/en-global/DSM/help/DSM/AdminCenter/file_share_privilege_nfs?version=7">the Synology documentation</a>:</p>
<blockquote>
<p>If <strong>AUTH_SYS</strong> security flavor is implemented: The client must have exactly the same numerical UID (user identifier) and GID (group identifier) on the NFS client and Synology NAS, or else the client will be assigned the permissions of <strong>others</strong> when accessing the shared folder. To avoid any permissions conflicts, you can select <strong>Map all users to admin</strong> from <strong>Squash</strong> or give &quot;Everyone&quot; permissions to the shared folder.</p>
</blockquote>
<p>Of course, the user and group identifiers weren&#39;t the same across Synology, my home lab server and my home computer. And I had no interest in trying to make them the same. The available squash options in Synology UI were also rather limited: map root to admin/guest or map all users to admin/guest. As such, they didn&#39;t give me an option to map the NFS user to my Synology user without manually tinkering with the <code>etc/exports</code> file which I also didn&#39;t want to do.</p>
<p>I took a step back and looked at other ways to access the repository on my Synology NAS. I liked SFTP best because:</p>
<ul>
<li>It&#39;s built into both Synology and restic.</li>
<li>I already used SSH to access Synology remotely.</li>
</ul>
<p>The process of switching from NFS to SFTP was very straightforward.</p>
<p>First, I enabled SFTP access on my Synology. This required two configuration changes in the <strong>Control Panel</strong>.</p>
<ul>
<li><strong>Enable SSH service</strong> in <strong>Terminal &amp; SNMP</strong> &gt; <strong>Terminal</strong>. Optionally change the <strong>Port</strong> as well. I had this one enabled already.
<img src="img/20260807-SynologyEnableSsh.png" alt="Enable SSH in Synology Control Panel"></li>
<li><strong>Enable SFTP service</strong> in <strong>File Services</strong> &gt; <strong>FTP</strong> &gt; <strong>SFTP</strong>.
<img src="img/20260807-SynologyEnableSftp.png" alt="Enable SFTP in Synology Control Panel"></li>
</ul>
<p>With Synology configured correctly, I was ready to configure the client. Since I already used SSH, I could skip some of the following steps, but I&#39;m listing them all anyway:</p>
<ul>
<li>I added an entry for my Synology to <code>~/.ssh/config</code> so that I didn&#39;t have to specify all the details every time I wanted to connect to it:<pre class="highlight"><code class="hljs nginx"><span class="hljs-title">Host</span> synology
    HostName synology
    Port <span class="hljs-number">2222</span>
    User damir
</code></pre></li>
<li>I created an SSH key so that I didn&#39;t have to enter the password every time:<pre class="highlight"><code class="hljs mathematica">ssh-keygen -t ed25519 -<span class="hljs-keyword">C</span> <span class="hljs-string">"me@mycomputer"</span>
</code></pre>
</li>
<li>I added the key to the authorized keys on Synology. This was the only time I had to enter my Synology password to connect to it. I didn&#39;t have to specify the username and port thanks to the <code>~/.ssh/config</code> entry above.<pre class="highlight"><code class="hljs stylus">ssh-copy-id -<span class="hljs-tag">i</span> ~/.ssh/id_ed25519<span class="hljs-class">.pub</span> synology
</code></pre>
</li>
<li>I changed my <code>RESTIC_REPOSITORY</code> to <code>sftp:synology:/restic</code> (again no username or port needed thanks to my SSH configuration).</li>
</ul>
<p>I followed the same steps on my home server with minor modifications because I had to do it for the root user:</p>
<ul>
<li>Add the SSH configuration entry to <code>/root/.ssh/config</code>.</li>
<li>Run all commands with <code>sudo</code>.</li>
<li>Set no passphrase for the SSH key so that it can be used from the backup script non-interactively.</li>
</ul>
<p>Since the ownership and permissions of existing files in the restic repository were in complete disarray, I had to fix those, too:</p>
<pre class="highlight"><code class="hljs groovy">sudo chown -R <span class="hljs-string">damir:</span>users <span class="hljs-regexp">/volume1/</span>restic/
sudo chmod -R a-rwx,u+rwX,g+rwX <span class="hljs-regexp">/volume1/</span>restic/
</code></pre>
<p>This was enough to get everything working. The backup created files as my Synology user (the one I used for SSH login). And I could access the restic repository from my home computer without root privileges.</p>
<p>As the final step, I got rid of everything NFS related because I didn&#39;t need it anymore:</p>
<ul>
<li>I unmounted the NFS share and deleted the mount directory on the client:<pre class="highlight"><code class="hljs bash"><span class="hljs-built_in">sudo</span> umount /mnt/restic
<span class="hljs-built_in">sudo</span> rm -rf /mnt/restic
</code></pre>
</li>
<li>Also on the client, I removed the entry for this mount from <code>etc/fstab/</code>.</li>
<li>I deleted the <strong>NFS Permissions</strong> entry for the <code>restic</code> <strong>Shared Folder</strong> in Synology <strong>Control Panel</strong>.</li>
</ul>
<p>Although at first NFS seemed the simplest option for accessing the restic repository on my Synology NAS, SFTP turned out a much better choice in the end. It only took me a year to realize that.</p>
]]></description>
            <link>https://www.damirscorner.com/blog/posts/20260807-SwitchingResticBackupFromNfsToSftp.html</link>
            <guid isPermaLink="true">https://www.damirscorner.com/blog/posts/20260807-SwitchingResticBackupFromNfsToSftp.html</guid>
            <dc:creator><![CDATA[Damir Arh]]></dc:creator>
            <pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>