TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #2074 2.03K
День 1707. #ЗаметкиНаПолях
Дискриминируемые Объединения в C#
Дискриминируемые объединения (discriminated unions) – один из давних запросов на включение в язык C#. В F# они используются давно, а C#-разработчикам придется подождать их ещё немного.

Дискриминируемые объединения позволяют сообщить компилятору (и другим инструментам, таким как IDE), что данные могут быть одним из ряда предопределённых типов. Например, у вас может быть метод RegisterUser(), который возвращает класс User, UserAlreadyExists или InvalidUsername. Эти классы не обязаны наследовать друг от друга. Вы просто хотите поддерживать 3 потенциальных типа возврата, сообщить об этом языку и получать ошибки компилятора, если возвращаете другой тип.

В минимальных API ASP.NET Core типы Results<> и TypedResults позволяют определить, какие типы объектов могут быть возвращены. Например, здесь может быть возвращён тип результат ApiItem или Unauthorized:
app.MapGet("/item/{id}", 
async Task<Results<Ok<ApiItem>, Unauthorized>>(
[FromRoute]int id,
GroceryListDb db) => {
// …
return TypedResults.Ok(item);
});

Тип Results<> по сути представляет собой дискриминируемое объединение: возвращаемое значение будет одним из (в данном случае) двух типов, и механизм минимальных API может использовать эту информацию для возврата правильного типа.

Упрощённо класс Results с поддержкой 2 типов выглядит так:
public class Results<TResult1, TResult2>
{
private Results(object result)
{
Result = result;
}

public object Result { get; }

public static implicit operator
Results<TResult1, TResult2>(TResult1 result)
=> new(result);

public static implicit operator
Results<TResult1, TResult2>(TResult2 result)
=> new(result);
}

Очевидно, что аналогично выглядят классы для 3, 4 и т.д. типов результата. Используя неявные операторы, класс Results может быть создан из любого типа, поддерживающего преобразования.

Вы можете использовать аналогичный класс в вашем коде, например, для создания метода, который может возвращать int, bool или строку, но ничего больше:
Results<int, bool, string> GetData() 
=> "Hello, world!";

Если тип результата будет другим, IDE (и компилятор) сообщат вам об ошибке. Даже сопоставление по шаблону доступно (для свойства Result):
var data = GetData();
var type = data.Result switch
{
int => "int",
bool => "bool",
string => "string",
_ => throw new NotImplementedException()
};

Console.WriteLine(type);

Недостатком является то, что если вы измените метод GetData(), чтобы он возвращал один из 4 типов (вместо 3), вы не получите ошибку компиляции в приведенном выше выражении switch. Это должно быть одним из преимуществ дискриминируемых объединений (если их когда-либо реализуют в языке): возможность получить инструментальную поддержку для этих случаев, информирующую вас о том, что у вас нет исчерпывающего соответствия для всех типов. Для минимальных API класс Results<> работает отлично. Использование результата является частью механики фреймворка, и в идеале вам никогда не придётся проводить исчерпывающее сравнение самостоятельно.

Вне минимальных API вы можете использовать пакет OneOf, который позволяет работать с дискриминируемыми объединениями, выдаёт ошибки компилятора, когда сравнения не являются исчерпывающими, и т. д.

Источник: https://blog.maartenballiauw.be/post/2023/09/18/discriminated-unions-in-csharp.html
  • 👍 13
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →