Weekly .NET Roundup: Union Types, Contextual Options, MAUI Labs
This week’s .NET items leaned toward “what’s next,” with early looks at language features and framework experiments that could change how you model APIs and configure apps. MAUI also clarified how to try new UI ideas without waiting for full releases. This split between stable baselines and previews/experiments continues from last week: alongside GA paths like Aspire on App Service, the .NET 11 Preview 2 thread keeps producing deeper language/runtime experiments, and MAUI is formalizing an “expect churn” lane through an experiments hub.
C# 15 union types in .NET 11 Preview
C# 15 (starting with .NET 11 Preview 2) introduces union types as a first-class way to define a closed set of value shapes without object, marker interfaces, or awkward base-class hierarchies. Following last week’s .NET 11 Preview 2 coverage, this is another Preview 2 feature that is likely to evolve as tooling and runtime pieces land. With union, you can declare public union Pet(Cat, Dog, Bird);, and the compiler treats the cases as complete: it supports implicit conversions from each case type (for example, Pet pet = new Dog("Rex");) and enforces exhaustive pattern matching in switch expressions. The maintenance benefit is clear: if you add a new case later, existing switches can warn when they are no longer exhaustive.
The preview includes important semantics and caveats. Patterns generally apply to the generated Value (auto-unwrapping), except var and _, which bind/match the union itself. Nullability matters: the default union value has Value == null, and the null pattern checks whether Value is null; if any cases are nullable (for example, Bird?), you must handle null explicitly for exhaustiveness. Under the hood, union is shorthand for a compiler-generated struct with per-case constructors and a Value of type object?, so value-type cases box by default.
For library authors and performance-sensitive code, “custom union types” are also supported. If you annotate a type with [System.Runtime.CompilerServices.Union] and follow the expected shape (public single-parameter constructors plus a public Value property), the compiler treats it as a union. Adding HasValue / TryGetValue can enable union-aware patterns that avoid boxing for value-type cases. To try it now, install the .NET 11 Preview SDK, target net11.0, set <LangVersion>preview</LangVersion>, and add runtime polyfills for UnionAttribute and IUnion (not included in .NET 11 Preview 2 yet). IDE support is expected via upcoming Visual Studio Insiders builds and is already in the latest C# Dev Kit Insiders.
Contextual options: per-request configuration via an experimental extensions package
The options pattern got an experimental add-on: Microsoft.Extensions.Options.Contextual, a NuGet package that adds a contextual layer on top of IOptions<T>. Building on last week’s theme of code-first workflows across more environments, it keeps configuration DI-driven while letting it adapt to request/tenant/user context. Instead of global or named options, you resolve IContextualOptions<TOptions, TContext> and call GetAsync(context) to compute options for a specific context. The walkthrough uses an ASP.NET Core “weather forecast” app with an AppContext (annotated [OptionsContext] and partial) carrying fields like UserId and Country, then derives defaults from context at the call site.
Mechanically, there are three parts: a source generator (ContextualOptionsGenerator) emits an IOptionsContext implementation; you implement an IOptionsContextReceiver that consumes key/value pairs via Receive<T>(string key, T value); and you register an additional contextual configure callback (IOptionsContext ctx, TOptions opts) to apply derived values. The post calls out a maintainability risk: receivers are coupled to context properties via string keys (property names), so renames can silently change behavior. There is also the cost of adopting an [Experimental] API: the package triggers EXTEXP0018 unless you opt in, and generated code is also experimental, so teams often suppress warnings broadly (for example, <NoWarn>$(NoWarn);EXTEXP0018</NoWarn>). The conclusion is to evaluate it mainly if you already rely on IOptions and need true per-context config that named options cannot express; otherwise feature flag tooling (Microsoft.FeatureManagement, OpenFeature) may fit better.
.NET MAUI’s new home for experiments: maui-labs
The .NET MAUI Community Standup introduced maui-labs as the official home for experimental and community-driven MAUI work, including prototypes and in-progress ideas not ready for stable MAUI. This fits the pattern we have been tracking: last week’s MAUI + Avalonia backend preview (Linux and WebAssembly) showed active experimentation around rendering and reach, and maui-labs clarifies where that work should live so teams can try it without confusing it with supported MAUI. The practical benefit is clearer boundaries: developers get one place to follow and test early work on expanded platform support, alternate rendering options, and exploratory features, with a clearer path for what might later graduate into the product.