
В жизни каждого C# программиста рано или поздно наступает момент, когда switch по object или иерархия классов начинают казаться костылями. Хочется просто вернуть «либо то, либо другое». В F# или Rust это база, а в C# — вечное ожидание.
Пока Microsoft обещает, сообщество плодит библиотеки. Но почему почти все они — это либо удар по производительности, либо нечитаемое месиво? Давайте разберемся, как устроены DU под капотом, и попробуем собрать свой вариант, который не стыдно засунуть в высоконагруженный проект или Unity‑игру.
Устройство DU в других языках
Си
Одним из самых простых примеров без «но» является Си. Действительно, язык просто имеет ключевое слово union, которое позволяет на один участок памяти наложить типы. Эта возможность просто комбинируется с enum или int тегом, чтобы различать, что мы записали.
Такой способ является самым оптимизированным, ведь оверхед идёт только на проверку тега, а запись и чтение идут просто в памяти
Код ниже — простая демонстрация на языке Си
enum ShapeType { CIRCLE, RECT };struct Shape { enum ShapeType kind; union { double radius; struct { double w, h; } rect; } data;};
Кажется, что это самый эффективный способ и в C# тоже можно использовать этот подход, но возникает проблема: В Си можно накладывать друг на друга любые типы, а в C# среда выполнения должна гарантировать, что ссылочный тип не накладывается на значимый тип, при нарушении ошибка TypeLoadException возникнет не на этапе компиляции, а при загрузке типа.
Код ниже на C# повторяет логику кода на Си.
public enum ShapeType : byte { Circle, Rect }[StructLayout(LayoutKind.Explicit)]public struct ShapeUnion{ [FieldOffset(0)] public ShapeType Kind; [FieldOffset(1)] public double Radius; [FieldOffset(1)] public (double Width, double Height) Rect;}
F#
В качестве концептуально другого примера возьмём F#. В нём DU — это просто наследование типов. При этом компилятор гарантирует, что других наследников у типа нет. Удобство заключается в том, что объединения имеют функции match и switch, которые заставляют программиста обработать абсолютно все варианты, чтобы избежать ошибок.
Ниже очень лаконичный код для определения объединения
type Shape = | Circle of radius: double | Rect of width: double * height: double
Конечно, выглядит слишком хорошо, почему бы не использовать это всегда? Дело в том, что каждое создание экземпляра такого типа — это аллокация объекта в куче. Банально это непроизводительно. Для сферы математического анализа (F#) это хорошее решение, но для того же веб приложения (C#) — это было бы неэффективно.
Сформулируем проблему
Проблема заключается в том, что для C# нет универсального решения, которое бы подошло всем.
-
Подход как в Си: Ограничение среды выполнения
-
Подход как в F#: Низкая производительность
Наивно можно придумать такой подход, при котором мы будем хранить тег и последовательно все возможные варианты. Он называется Composition.
public enum ShapeType : byte { Circle, Rect }public struct Shape{ public ShapeType tag; public double radius; public (double Width, double Height) Rect;}
Я думаю вы уже ожидаете, что будет какой‑то подвох. Он заключается в том, что такая структура имеет наибольший из всех размер, ведь мы резервируем место под каждый вариант, а используем по факту только 1. Казалось бы, лишние 20 байт погоды не сделают, но такая структура с большим размером обречена не влазить в кэш‑линию и иметь оверхед на обнуление каждого поля.
Уже можно было подумать, что есть конкретные 3 варианта и иначе никак, но мы можем комбинировать их для достижения нужных целей.
Таким можно сделать компактную и быструю структуру с таким принципом:
-
Все значимые типы накладываем друг на друга
-
Все ссылочные типы кастятся к
object
Такой подход быстрый, но по памяти не идеален, хотя всё ещё лучше композиции.
Не спешите. Возникает вопрос: как это реализовать? Написать код для конкретного случая легко, но что делать, если нужен шаблон? Тут возникают сложности, ведь заранее неизвестно, какие типы данных будут использоваться, и поэтому невозможно решить, где и как их хранить. Кроме того, тип T нельзя использовать в явном виде, поскольку его размер заранее неизвестен.
Проблема в том, что автоматизация невозможна, а писать код вручную очень долго, так как каждый раз его нужно адаптировать. Решение — использовать Source Generator. Он инструменты набирает популярность, поскольку может генерировать большое количество правильного кода, который иначе пришлось бы писать вручную. В практической части я покажу свою реализацию генератора.
Какие есть решения для DU сейчас?
Самое известное решение — mcintyre321 OneOf
(Гитхаб) Оно использует подход композиции, то есть все типы хранятся последовательно друг за другом. Это одновременно самый ленивый и самый надёжный путь. Взаимодействие с заготовленными типами идёт через готовые структуры OneOf<T0... T7> с поддержкой до 8 вариантов (до 32 с библиотекой‑расширением).
Так выглядит объявление полей:
public readonly struct OneOf<T0, T1, T2> : IOneOf{private readonly T0 _value0;private readonly T1 _value1;private readonly T2 _value2;private readonly int _index;...
Плюсы:
-
Имеет готовые функции
MatchиSwitch, которые обязывают обработать все варианты. -
Имеются удобные касты
-
Небольшой код
Минусы:
-
Большой размер структуры
-
Нет именованных полей, значит придётся запоминать, что значит
AsT0и т.д -
Не самая высокая скорость
-
Если в вариантах пустая структура, то она всё равно займёт байт
-
Нет поддержки сериализаторов
-
Нельзя дополнить код (кроме как расширениями)
Итог: Эта библиотека сейчас стандарт для тех, кто не хочет сильно заморачиваться, она даёт готовый функционал и подходит для большинства бытовых случаев, хотя минусы тоже есть. Отсутствие именных полей очень сильно бьёт по читаемости кода
Более современное решение — domn1995 DuNet
(Гитхаб) Оно использует вложенные record типы и через Source Generator создаёт методы. Здесь для хранения используется подход как в F#, то есть наследование. Главная фишка библиотеки — красивый синтаксис.
Объявление:
[Union]partial record Shape{ partial record Circle(double Radius); partial record Rectangle(double Length, double Width); partial record Triangle(double Base, double Height);}
Плюсы:
-
Красивый синтаксис
-
Генерируется уникальный код с настраиваемыми названиями
-
Поддерживает Json сериализацию
Минусы:
-
Создание объекта — всегда аллокация
-
Нельзя использовать сторонние типы, только определять новые внутри
-
Низкая скорость
Итог: Библиотека для тех, кому красота синтаксиса превыше всего, подойдёт для ненагруженных приложений.
А что обещают в самом C#?
Уже много лет Microsoft обещают ввести DU в сам язык. И вправду, в том же F# они были с самого начала.
Возможный синтаксис определения:
public record class Circle(double Radius);public record class Rectangle((double Weight, double Height) Rect);public union Pet(Circle, Rectangle);
Скорее всего это будет то же самое наследование, что и в F#, но есть шанс, что будет использоваться подход с наложением там, где это возможно.
Также обещают добавить анонимные союзы
public (int | string) GetValue() ...
На самом деле этого бы хватало в большинстве случаев, а главное — стандартизация: авторы библиотек будут использовать один и тот же инструмент для DU.
В чём же проблема?
Проблема заключается в том, что поддержка объединений будет только начиная с версии на которой они вышли. Это потенциально отрезает большую часть аудитории, например движка Unity (который до сих пор на .netstandart 2.1) или многих библиотек, которые поддерживают старые рантаймы. Для просто многих будет невыгодно переписывать библиотеки, ведь им придётся ограничиться лишь новой версией языка.
Создаём велосипед
Логичным кажется после того, как забраковать все готовые решения, предложить своё.
Какие проблемы нужно решить?
-
Нужно сделать настраиваемые названия типов, что улучшит чтение кода
-
Нужно сделать возможность самому дописывать логику
-
Добавить поддержку сериализаторов Json и MemoryPack
-
Дать пользователю решать, как компоновать варианты в памяти
-
Не выделять место под варианты без значения (Void типы)
-
Обеспечить хорошую производительность
-
Сделать поддержку инспектора Unity
-
Сделать интеграцию с функциями языка
-
Метаданные без рефлексии
Список довольно большой и поэтому нужно разбирать по‑порядку
Как объявлять новый тип?
Т.к это SourceGenerator, все настройки передаются через атрибут, а типы через интерфейс.
В настройках передаются названия полей, которые потом будут использоваться.
Пример:
[GenerateOneOf(["Number", "Text"], AllowEmpty: false, OneOfLayoutKind.Auto, KindPosition.After, KindSize.Int, GenerateInfo: true)]public readonly partial struct Numeric : IOneOf<double, string>
Выбор лаяута
Так как в начале статьи мы выяснили, что нет лучшего способа хранения вариантов, поэтому нужно дать пользователю выбор или автоматически решить за него, какой вариант комопоновки полей использовать.
Я выделил 4 варианта, которые покрывают основные ситуации:
-
ExplicitUnion— все значимые типы накладываются друг на друга, а ссылочные кастятся к object. К сожалению способ не работает для генериков (ведь их нельзя накладывать) -
Boxing— вообще любые типы кастим к object. если изначально все ссылочные, то оверхеда нет, иначе возможны аллокации -
Composition— все типы храним друг за другом. работает всегда и со всем -
Hybrid— все значимые типы и генерики храним друг за другом, а ссылочные кастим к object
Void типы
Часто возникает такая ситуация, когда нужно иметь вариант, который не несёт нагрузки, для этого пустую структуру нужно пометить [VoidType]. для таких структур не выделяется место в памяти, а все функции получения значения возвращают default.
Основные функции (X — название поля)
-
Switch— обработать все варианты без возврата значения -
Match— обработать все варианты с возвратом значения -
TryGet/TryPick— попробовать получить значение -
/— свойства для получения значенияOrDefault
-
Deconstruct,ToString,GetHashCodeи много функций для удобства
Метаданные
Я сделал, чтобы генератор создавал внутри структуры по желанию пользователя статический класс с внутренними данными о структуре (используемые типы, количество вариантов и т.д)
Сериализаторы
У сторонних библиотек нет поддержки сериализаторов. Я реализовал поддержку Newtonsoft.Json, System.Text.Json и MemoryPack. Для этого нужно просто повесить специальные атрибуты
Сериализаторы для Json не создают кучу лишних полей, а лишь только нужные, а MemoryPack записывает всё максимально компактно
Unity
Поддержка инспектора unity — та ещё проблема, потому что юнити не умеет сериализовать сложные объекты. Пришлось делать обёртку с кастомной отрисовкой, чтобы выглядело максимально нативно.
Производительность
Используя ExplicitUnion получилось ускорить создание объекта по сравнению с mcintyre321 OneOf на 15%. Match стал быстрее на 4%, а ручной перебор через switch на все 9%. Для такой небольшой задачи даже несколько процентов прироста колосальны
Пока что лучше OneOf только ExplicitUnion. Composition находится примерно на одном уровне с OneOf.
Если вам интересна моя реализация, вы можете посетить GitHub и помочь проекту: https://github.com/SashaGonch228/DotPointers.OneOf.
Я планирую развивать проект. Возможно, добавлю поддержку protobuf, EfCore, MessagePack и других сериализаторов, а также оптимизирую код.
Пример работы генератора
Исходный код:
[GenerateOneOf(["Single", "Between", "All", "None"], false)]public readonly partial struct Range : IOneOf<int, (int, int), All, None>{[MethodImpl(MethodImplOptions.AggressiveInlining)]public bool InRange(int value) => Match(single => value == single,between => value >= between.Item1 && value <= between.Item2,all => true,none => false);}
Генерируемый код:
// <auto-generated/> by OneOfGenerator// Layout: ExplicitUnion because Auto: Multiple structures detected, using union to save space#nullable enableusing System;using System.Linq;using System.Collections;using System.Collections.Generic;using System.Runtime.InteropServices;using System.Runtime.CompilerServices;using System.Diagnostics;using System.Diagnostics.CodeAnalysis;using System.Threading.Tasks;using DotPointers;using DotPointers.OneOf;namespace DotPointers.OneOf.Types{/// <summary>/// Discriminated union [(int: "Single"), ((int, int): "Between"), (All: "All"), (None: "None")]/// </summary>[DebuggerDisplay("{DebuggerDisplay,nq}")][CompilerGenerated]partial struct Range : IEquatable<Range>, IOneOfMetadata{[StructLayout(LayoutKind.Explicit)]internal struct __DataUnion_Range{[FieldOffset(0)] public int _v0;[FieldOffset(0)] public (int, int) _v1;}#pragma warning disable CS0649private readonly __DataUnion_Range _data;#pragma warning restore CS0649private readonly OneOfRange _kind;#pragma warning disable CS8618/// <summary>/// Internal constructor. Initializes the union with the specified kind and underlying data structure according to the determined layout./// </summary>[MethodImpl(MethodImplOptions.AggressiveInlining)]private Range(OneOfRange kind, __DataUnion_Range data){Unsafe.SkipInit(out this);this._kind = kind;this._data = data;}/// <summary>/// Initializes an empty instance of the union. Throws an exception since this union does not allow empty states./// </summary>/// <exception cref="InvalidOperationException">Always thrown.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public Range(){Unsafe.SkipInit(out this);OneOfThrowHelper.ThrowEmpty(nameof(Range));}/// <summary>/// Initializes a new instance of Range with a value of type <see cref="int" />./// </summary>/// <param name="value">The initial value.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public Range(int value){Unsafe.SkipInit(out this);global::System.Runtime.CompilerServices.Unsafe.SkipInit(out this._data);this._data._v0 = value;this._kind = OneOfRange.Single;}/// <summary>/// Initializes a new instance of Range with a value of type <see cref="(int, int)" />./// </summary>/// <param name="value">The initial value.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public Range((int, int) value){Unsafe.SkipInit(out this);global::System.Runtime.CompilerServices.Unsafe.SkipInit(out this._data);this._data._v1 = value;this._kind = OneOfRange.Between;}/// <summary>/// Initializes a new instance of Range with a value of type <see cref="global::DotPointers.OneOf.All" />./// </summary>/// <param name="value">The initial value.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public Range(global::DotPointers.OneOf.All value){Unsafe.SkipInit(out this);this._kind = OneOfRange.All;}/// <summary>/// Initializes a new instance of Range with a value of type <see cref="global::DotPointers.OneOf.None" />./// </summary>/// <param name="value">The initial value.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public Range(global::DotPointers.OneOf.None value){Unsafe.SkipInit(out this);this._kind = OneOfRange.None;}#pragma warning restore CS8618/// <summary>/// Creates a new instance of Range from a value of type <see cref="int" />./// </summary>/// <param name="obj">The value to wrap.</param>/// <returns>A new Range instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Range FromSingle(int obj) => new(obj);/// <summary>/// Creates a new instance of Range from a value of type <see cref="(int, int)" />./// </summary>/// <param name="obj">The value to wrap.</param>/// <returns>A new Range instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Range FromBetween((int, int) obj) => new(obj);/// <summary>/// Creates a new instance of Range representing the <see cref="All" /> state./// </summary>/// <returns>A new Range instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Range FromAll() => new(default(global::DotPointers.OneOf.All));/// <summary>/// Creates a new instance of Range representing the <see cref="None" /> state./// </summary>/// <returns>A new Range instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Range FromNone() => new(default(global::DotPointers.OneOf.None));private readonly int SingleForce => _data._v0;private readonly (int, int) BetweenForce => _data._v1;private readonly global::DotPointers.OneOf.All AllForce => default(global::DotPointers.OneOf.All);private readonly global::DotPointers.OneOf.None NoneForce => default(global::DotPointers.OneOf.None);/// <summary>/// Gets the kind of the current value./// </summary>public readonly OneOfRange Kind => _kind;/// <summary>/// Gets the integer index of the current kind./// </summary>public readonly int Index => (int)_kind;/// <summary>/// Gets the boxed value of the current instance./// </summary>public readonly object? BoxValue => _kind switch{OneOfRange.Single => (object?)SingleForce,OneOfRange.Between => (object?)BetweenForce,OneOfRange.All => (object?)AllForce,OneOfRange.None => (object?)NoneForce,_ => null,};/// <summary>/// Gets the value of the <see cref="Single" /> member./// </summary>/// <returns>The value of the <see cref="Single" /> member.</returns>/// <exception cref="InvalidOperationException">Thrown if the union instance does not currently hold the <see cref="Single" /> type.</exception>public readonly int Single{[MethodImpl(MethodImplOptions.AggressiveInlining)]get{if (_kind != OneOfRange.Single){OneOfThrowHelper.ThrowInvalid<OneOfRange>(0, (int)_kind);}return _data._v0;}}/// <summary>/// Gets the value of the <see cref="Single" /> member, or the default value if the union does not hold this type./// </summary>/// <returns>The value of the <see cref="Single" /> member, or default.</returns>public readonly int? SingleOrDefault => _kind == OneOfRange.Single ? _data._v0: default;/// <summary>/// Gets the value of the <see cref="Between" /> member./// </summary>/// <returns>The value of the <see cref="Between" /> member.</returns>/// <exception cref="InvalidOperationException">Thrown if the union instance does not currently hold the <see cref="Between" /> type.</exception>public readonly (int, int) Between{[MethodImpl(MethodImplOptions.AggressiveInlining)]get{if (_kind != OneOfRange.Between){OneOfThrowHelper.ThrowInvalid<OneOfRange>(1, (int)_kind);}return _data._v1;}}/// <summary>/// Gets the value of the <see cref="Between" /> member, or the default value if the union does not hold this type./// </summary>/// <returns>The value of the <see cref="Between" /> member, or default.</returns>public readonly (int, int)? BetweenOrDefault => _kind == OneOfRange.Between ? _data._v1: default;/// <summary>/// Gets the value of the <see cref="All" /> member./// </summary>/// <returns>The value of the <see cref="All" /> member.</returns>/// <exception cref="InvalidOperationException">Thrown if the union instance does not currently hold the <see cref="All" /> type.</exception>public readonly global::DotPointers.OneOf.All All{[MethodImpl(MethodImplOptions.AggressiveInlining)]get{if (_kind != OneOfRange.All){OneOfThrowHelper.ThrowInvalid<OneOfRange>(2, (int)_kind);}return default(global::DotPointers.OneOf.All);}}/// <summary>/// Gets the value of the <see cref="All" /> member, or the default value if the union does not hold this type./// </summary>/// <returns>The value of the <see cref="All" /> member, or default.</returns>public readonly global::DotPointers.OneOf.All? AllOrDefault => _kind == OneOfRange.All ? default(global::DotPointers.OneOf.All): default;/// <summary>/// Gets the value of the <see cref="None" /> member./// </summary>/// <returns>The value of the <see cref="None" /> member.</returns>/// <exception cref="InvalidOperationException">Thrown if the union instance does not currently hold the <see cref="None" /> type.</exception>public readonly global::DotPointers.OneOf.None None{[MethodImpl(MethodImplOptions.AggressiveInlining)]get{if (_kind != OneOfRange.None){OneOfThrowHelper.ThrowInvalid<OneOfRange>(3, (int)_kind);}return default(global::DotPointers.OneOf.None);}}/// <summary>/// Gets the value of the <see cref="None" /> member, or the default value if the union does not hold this type./// </summary>/// <returns>The value of the <see cref="None" /> member, or default.</returns>public readonly global::DotPointers.OneOf.None? NoneOrDefault => _kind == OneOfRange.None ? default(global::DotPointers.OneOf.None): default;/// <summary>/// Gets a value indicating whether this instance holds the type <see cref="Single" />./// </summary>public readonly bool IsSingle => _kind == OneOfRange.Single;/// <summary>/// Gets a value indicating whether this instance holds the type <see cref="Between" />./// </summary>public readonly bool IsBetween => _kind == OneOfRange.Between;/// <summary>/// Gets a value indicating whether this instance holds the type <see cref="All" />./// </summary>public readonly bool IsAll => _kind == OneOfRange.All;/// <summary>/// Gets a value indicating whether this instance holds the type <see cref="None" />./// </summary>public readonly bool IsNone => _kind == OneOfRange.None;/// <summary>/// Attempts to get the value of the <see cref="Single" /> member./// </summary>/// <param name="OutValue">When this method returns true, contains the value.</param>/// <returns>True if the union currently holds the <see cref="Single" /> type; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryGetSingle([NotNullWhen(true)] out int OutValue){if (_kind == OneOfRange.Single){OutValue = _data._v0;return true;}OutValue = default!;return false;}/// <summary>/// Attempts to get the value of the <see cref="Between" /> member./// </summary>/// <param name="OutValue">When this method returns true, contains the value.</param>/// <returns>True if the union currently holds the <see cref="Between" /> type; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryGetBetween([NotNullWhen(true)] out (int, int) OutValue){if (_kind == OneOfRange.Between){OutValue = _data._v1;return true;}OutValue = default!;return false;}/// <summary>/// Attempts to get the value of the <see cref="All" /> member./// </summary>/// <param name="OutValue">When this method returns true, contains the value.</param>/// <returns>True if the union currently holds the <see cref="All" /> type; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryGetAll([NotNullWhen(true)] out global::DotPointers.OneOf.All OutValue){if (_kind == OneOfRange.All){OutValue = default(global::DotPointers.OneOf.All);return true;}OutValue = default!;return false;}/// <summary>/// Attempts to get the value of the <see cref="None" /> member./// </summary>/// <param name="OutValue">When this method returns true, contains the value.</param>/// <returns>True if the union currently holds the <see cref="None" /> type; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryGetNone([NotNullWhen(true)] out global::DotPointers.OneOf.None OutValue){if (_kind == OneOfRange.None){OutValue = default(global::DotPointers.OneOf.None);return true;}OutValue = default!;return false;}/// <summary>/// Attempts to extract the value of the <see cref="Single" /> member, returning the remainder of the union if successful./// </summary>/// <returns>True if the union currently holds the <see cref="Single" /> type; otherwise, false.</returns>/// <param name="OutValue">When this method returns true, contains the value of the <see cref="Single" /> member.</param>/// <param name="Remainder">When this method returns false, contains the remaining union instance.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryPickSingle([NotNullWhen(true)] out int OutValue, [NotNullWhen(false)] out Range Remainder){if (_kind == OneOfRange.Single){OutValue = _data._v0;Remainder = default!;return true;}Remainder = this;OutValue = default!;return false;}/// <summary>/// Attempts to extract the value of the <see cref="Between" /> member, returning the remainder of the union if successful./// </summary>/// <returns>True if the union currently holds the <see cref="Between" /> type; otherwise, false.</returns>/// <param name="OutValue">When this method returns true, contains the value of the <see cref="Between" /> member.</param>/// <param name="Remainder">When this method returns false, contains the remaining union instance.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryPickBetween([NotNullWhen(true)] out (int, int) OutValue, [NotNullWhen(false)] out Range Remainder){if (_kind == OneOfRange.Between){OutValue = _data._v1;Remainder = default!;return true;}Remainder = this;OutValue = default!;return false;}/// <summary>/// Attempts to extract the value of the <see cref="All" /> member, returning the remainder of the union if successful./// </summary>/// <returns>True if the union currently holds the <see cref="All" /> type; otherwise, false.</returns>/// <param name="OutValue">When this method returns true, contains the value of the <see cref="All" /> member.</param>/// <param name="Remainder">When this method returns false, contains the remaining union instance.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryPickAll([NotNullWhen(true)] out global::DotPointers.OneOf.All OutValue, [NotNullWhen(false)] out Range Remainder){if (_kind == OneOfRange.All){OutValue = default(global::DotPointers.OneOf.All);Remainder = default!;return true;}Remainder = this;OutValue = default!;return false;}/// <summary>/// Attempts to extract the value of the <see cref="None" /> member, returning the remainder of the union if successful./// </summary>/// <returns>True if the union currently holds the <see cref="None" /> type; otherwise, false.</returns>/// <param name="OutValue">When this method returns true, contains the value of the <see cref="None" /> member.</param>/// <param name="Remainder">When this method returns false, contains the remaining union instance.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool TryPickNone([NotNullWhen(true)] out global::DotPointers.OneOf.None OutValue, [NotNullWhen(false)] out Range Remainder){if (_kind == OneOfRange.None){OutValue = default(global::DotPointers.OneOf.None);Remainder = default!;return true;}Remainder = this;OutValue = default!;return false;}/// <summary>/// Executes a specified action based on the current type held by the union./// </summary>/// <param name="OnSingle">The action to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The action to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The action to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The action to execute if the union holds the <see cref="None" /> type.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly void Switch(Action<int> OnSingle, Action<(int, int)> OnBetween, Action<global::DotPointers.OneOf.All> OnAll, Action<global::DotPointers.OneOf.None> OnNone){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { OnSingle.Invoke(_data._v0); return; }if (kind == OneOfRange.Between && OnBetween != null) { OnBetween.Invoke(_data._v1); return; }if (kind == OneOfRange.All && OnAll != null) { OnAll.Invoke(default(global::DotPointers.OneOf.All)); return; }if (kind == OneOfRange.None && OnNone != null) { OnNone.Invoke(default(global::DotPointers.OneOf.None)); return; }OneOfThrowHelper.ThrowEmpty("Range");}/// <summary>/// Executes a specified function based on the current type held by the union and returns the result./// </summary>/// <returns>The result of the executed function.</returns>/// <param name="OnSingle">The function to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The function to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The function to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The function to execute if the union holds the <see cref="None" /> type.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly TResult Match<TResult>(Func<int, TResult> OnSingle, Func<(int, int), TResult> OnBetween, Func<global::DotPointers.OneOf.All, TResult> OnAll, Func<global::DotPointers.OneOf.None, TResult> OnNone){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { return OnSingle.Invoke(_data._v0); }if (kind == OneOfRange.Between && OnBetween != null) { return OnBetween.Invoke(_data._v1); }if (kind == OneOfRange.All && OnAll != null) { return OnAll.Invoke(default(global::DotPointers.OneOf.All)); }if (kind == OneOfRange.None && OnNone != null) { return OnNone.Invoke(default(global::DotPointers.OneOf.None)); }return OneOfThrowHelper.ThrowEmpty<TResult>("Range");}/// <summary>/// Executes a specified action based on the current type held by the union, passing a custom context./// </summary>/// <param name="OnSingle">The action to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The action to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The action to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The action to execute if the union holds the <see cref="None" /> type.</param>/// <param name="context">The state to pass to the action.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly void Switch<TContext>(Action<int, TContext> OnSingle, Action<(int, int), TContext> OnBetween, Action<global::DotPointers.OneOf.All, TContext> OnAll, Action<global::DotPointers.OneOf.None, TContext> OnNone, TContext context){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { OnSingle.Invoke(_data._v0, context); return; }if (kind == OneOfRange.Between && OnBetween != null) { OnBetween.Invoke(_data._v1, context); return; }if (kind == OneOfRange.All && OnAll != null) { OnAll.Invoke(default(global::DotPointers.OneOf.All), context); return; }if (kind == OneOfRange.None && OnNone != null) { OnNone.Invoke(default(global::DotPointers.OneOf.None), context); return; }OneOfThrowHelper.ThrowEmpty("Range");}/// <summary>/// Executes a specified function based on the current type held by the union, passing a custom context, and returns the result./// </summary>/// <returns>The result of the executed function.</returns>/// <param name="OnSingle">The function to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The function to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The function to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The function to execute if the union holds the <see cref="None" /> type.</param>/// <param name="context">The state to pass to the function.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly TResult Match<TResult, TContext>(Func<int, TContext, TResult> OnSingle, Func<(int, int), TContext, TResult> OnBetween, Func<global::DotPointers.OneOf.All, TContext, TResult> OnAll, Func<global::DotPointers.OneOf.None, TContext, TResult> OnNone, TContext context){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { return OnSingle.Invoke(_data._v0, context); }if (kind == OneOfRange.Between && OnBetween != null) { return OnBetween.Invoke(_data._v1, context); }if (kind == OneOfRange.All && OnAll != null) { return OnAll.Invoke(default(global::DotPointers.OneOf.All), context); }if (kind == OneOfRange.None && OnNone != null) { return OnNone.Invoke(default(global::DotPointers.OneOf.None), context); }return OneOfThrowHelper.ThrowEmpty<TResult>("Range");}/// <summary>/// Asynchronously executes the action corresponding to the current type held by the union./// </summary>/// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>/// <param name="OnSingle">The async action to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The async action to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The async action to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The async action to execute if the union holds the <see cref="None" /> type.</param>/// <remarks>If the union is empty and no corresponding action is provided, this method throws an exception.</remarks>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly async ValueTask SwitchAsync(Func<int, ValueTask> OnSingle, Func<(int, int), ValueTask> OnBetween, Func<global::DotPointers.OneOf.All, ValueTask> OnAll, Func<global::DotPointers.OneOf.None, ValueTask> OnNone){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { await OnSingle.Invoke(_data._v0); return; }if (kind == OneOfRange.Between && OnBetween != null) { await OnBetween.Invoke(_data._v1); return; }if (kind == OneOfRange.All && OnAll != null) { await OnAll.Invoke(default(global::DotPointers.OneOf.All)); return; }if (kind == OneOfRange.None && OnNone != null) { await OnNone.Invoke(default(global::DotPointers.OneOf.None)); return; }OneOfThrowHelper.ThrowEmpty("Range");}/// <summary>/// Asynchronously executes the function corresponding to the current type held by the union, returning a result./// </summary>/// <returns>A <see cref="ValueTask{{TResult}}" /> representing the asynchronous operation.</returns>/// <param name="OnSingle">The async function to execute if the union holds the <see cref="Single" /> type.</param>/// <param name="OnBetween">The async function to execute if the union holds the <see cref="Between" /> type.</param>/// <param name="OnAll">The async function to execute if the union holds the <see cref="All" /> type.</param>/// <param name="OnNone">The async function to execute if the union holds the <see cref="None" /> type.</param>/// <exception cref="InvalidOperationException">Thrown if the union instance is empty and no OnEmpty function is provided.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly async ValueTask<TResult> MatchAsync<TResult>(Func<int, ValueTask<TResult>> OnSingle, Func<(int, int), ValueTask<TResult>> OnBetween, Func<global::DotPointers.OneOf.All, ValueTask<TResult>> OnAll, Func<global::DotPointers.OneOf.None, ValueTask<TResult>> OnNone){var kind = _kind;if (kind == OneOfRange.Single && OnSingle != null) { return await OnSingle.Invoke(_data._v0); }if (kind == OneOfRange.Between && OnBetween != null) { return await OnBetween.Invoke(_data._v1); }if (kind == OneOfRange.All && OnAll != null) { return await OnAll.Invoke(default(global::DotPointers.OneOf.All)); }if (kind == OneOfRange.None && OnNone != null) { return await OnNone.Invoke(default(global::DotPointers.OneOf.None)); }return OneOfThrowHelper.ThrowEmpty<TResult>("Range");}/// <summary>/// Returns a new Range instance where the current member's value has been transformed by the mapper function./// </summary>/// <param name="mapper">The function to apply to the current member's value.</param>/// <returns>A new Range instance containing the mapped value, or this instance if it does not hold the Single type.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly Range MapSingle(Func<int, int> mapper){if (_kind == OneOfRange.Single){return new Range(mapper.Invoke(_data._v0));}return this;}/// <summary>/// Returns a new Range instance where the current member's value has been transformed by the mapper function./// </summary>/// <param name="mapper">The function to apply to the current member's value.</param>/// <returns>A new Range instance containing the mapped value, or this instance if it does not hold the Between type.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly Range MapBetween(Func<(int, int), (int, int)> mapper){if (_kind == OneOfRange.Between){return new Range(mapper.Invoke(_data._v1));}return this;}/// <summary>/// Determines whether the specified union instance is equal to the current instance./// </summary>/// <param name="other">The union instance to compare with.</param>/// <returns>True if equal; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly bool Equals(Range other){if (_kind != other._kind) { return false; }return _kind switch{OneOfRange.Single => EqualityComparer<int>.Default.Equals(_data._v0, other.SingleForce),OneOfRange.Between => EqualityComparer<(int, int)>.Default.Equals(_data._v1, other.BetweenForce),OneOfRange.All => true,OneOfRange.None => true,_ => true};}/// <summary>/// Determines whether the specified object is equal to the current instance./// </summary>/// <param name="obj">The object to compare with.</param>/// <returns>True if equal; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly override bool Equals(object? obj) => obj is Range other && Equals(other);/// <summary>/// Returns the hash code for this instance./// </summary>/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly override int GetHashCode(){unchecked{var kind = _kind;int hash = 17;hash = hash * 23 + (int)kind;if (kind == OneOfRange.Single) { hash = hash * 23 + (_data._v0).GetHashCode(); }else if (kind == OneOfRange.Between) { hash = hash * 23 + (_data._v1).GetHashCode(); }return hash;}}/// <summary>/// Determines whether two union instances are equal./// </summary>/// <returns>True if equal; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static bool operator ==(Range left, Range right) => left.Equals(right);/// <summary>/// Determines whether two union instances are not equal./// </summary>/// <returns>True if not equal; otherwise, false.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static bool operator !=(Range left, Range right) => !left.Equals(right);/// <summary>/// Deconstructs the union into its kind and values./// </summary>/// <param name="kind">The kind of the current union instance.</param>/// <param name="single">The out variable for the <see cref="Single" /> member.</param>/// <param name="between">The out variable for the <see cref="Between" /> member.</param>/// <param name="all">The out variable for the <see cref="All" /> member.</param>/// <param name="none">The out variable for the <see cref="None" /> member.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly void Deconstruct(out OneOfRange kind, out int? single, out (int, int)? between, out global::DotPointers.OneOf.All? all, out global::DotPointers.OneOf.None? none){kind = _kind;single = IsSingle ? _data._v0 : default;between = IsBetween ? _data._v1 : default;all = IsAll ? default(global::DotPointers.OneOf.All) : default;none = IsNone ? default(global::DotPointers.OneOf.None) : default;}/// <summary>/// Implicitly converts a value of type <see cref="int" /> to a Range instance./// </summary>/// <param name="value">The value to convert.</param>/// <returns>A new union instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static implicit operator Range(int value) => new(value);/// <summary>/// Explicitly converts a Range instance to a value of type <see cref="int" />./// </summary>/// <param name="source">The union instance to convert.</param>/// <returns>The underlying value.</returns>/// <exception cref="InvalidOperationException">Thrown if the union does not hold the requested type.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static explicit operator int(Range source) => source.Single;/// <summary>/// Implicitly converts a value of type <see cref="(int, int)" /> to a Range instance./// </summary>/// <param name="value">The value to convert.</param>/// <returns>A new union instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static implicit operator Range((int, int) value) => new(value);/// <summary>/// Explicitly converts a Range instance to a value of type <see cref="(int, int)" />./// </summary>/// <param name="source">The union instance to convert.</param>/// <returns>The underlying value.</returns>/// <exception cref="InvalidOperationException">Thrown if the union does not hold the requested type.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static explicit operator (int, int)(Range source) => source.Between;/// <summary>/// Implicitly converts a value of type <see cref="global::DotPointers.OneOf.All" /> to a Range instance./// </summary>/// <param name="value">The value to convert.</param>/// <returns>A new union instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static implicit operator Range(global::DotPointers.OneOf.All value) => new(value);/// <summary>/// Explicitly converts a Range instance to a value of type <see cref="global::DotPointers.OneOf.All" />./// </summary>/// <param name="source">The union instance to convert.</param>/// <returns>The underlying value.</returns>/// <exception cref="InvalidOperationException">Thrown if the union does not hold the requested type.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static explicit operator global::DotPointers.OneOf.All(Range source) => source.All;/// <summary>/// Implicitly converts a value of type <see cref="global::DotPointers.OneOf.None" /> to a Range instance./// </summary>/// <param name="value">The value to convert.</param>/// <returns>A new union instance.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static implicit operator Range(global::DotPointers.OneOf.None value) => new(value);/// <summary>/// Explicitly converts a Range instance to a value of type <see cref="global::DotPointers.OneOf.None" />./// </summary>/// <param name="source">The union instance to convert.</param>/// <returns>The underlying value.</returns>/// <exception cref="InvalidOperationException">Thrown if the union does not hold the requested type.</exception>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static explicit operator global::DotPointers.OneOf.None(Range source) => source.None;/// <summary>/// Returns an enumerable containing the current instance if it is not empty./// </summary>/// <returns>A single-element enumerable or an empty enumerable.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly DotPointers.OneOf.SingleEnumerable<Range> AsEnumerable() => new(this);/// <summary>/// Returns a string that represents the current object./// </summary>/// <returns>A string representation of the current object.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly override string? ToString() => _kind switch{OneOfRange.Single => (_data._v0).ToString(),OneOfRange.Between => (_data._v1).ToString(),OneOfRange.All => (default(global::DotPointers.OneOf.All)).ToString(),OneOfRange.None => (default(global::DotPointers.OneOf.None)).ToString(),_ => "Empty"};#if DEBUGprivate readonly string DebuggerDisplay => _kind switch{OneOfRange.Single => $"int({Single}) - {Unsafe.SizeOf<Range>()} bytes",OneOfRange.Between => $"(int, int)({Between}) - {Unsafe.SizeOf<Range>()} bytes",OneOfRange.All => $"All({All}) - {Unsafe.SizeOf<Range>()} bytes",OneOfRange.None => $"None({None}) - {Unsafe.SizeOf<Range>()} bytes",_ => "Empty"};#endif/// <summary>/// Starts a method chain for conditional execution based on the union's value./// </summary>/// <returns>A chain context to continue the execution.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Chain() => new(this);/// <summary>/// Represents a context for chaining conditional operations on the union./// </summary>public readonly ref struct ChainContext{private readonly Range _source;private readonly bool _consumed;private readonly bool _matched;[MethodImpl(MethodImplOptions.AggressiveInlining)]internal ChainContext(Range source, bool consumed = false, bool matched = false){_source = source;_consumed = consumed;_matched = matched;}/// <summary>/// Evaluates a predicate if the union holds the <see cref="Single" /> type./// </summary>/// <param name="predicate">The predicate to evaluate.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext IfSingle(Predicate<int> predicate){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.Single && predicate.Invoke(_source.SingleForce));}/// <summary>/// Checks if the union holds the <see cref="Single" /> type./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public ChainContext IfSingle(){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.Single);}/// <summary>/// Evaluates a predicate if the union holds the <see cref="Between" /> type./// </summary>/// <param name="predicate">The predicate to evaluate.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext IfBetween(Predicate<(int, int)> predicate){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.Between && predicate.Invoke(_source.BetweenForce));}/// <summary>/// Checks if the union holds the <see cref="Between" /> type./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public ChainContext IfBetween(){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.Between);}/// <summary>/// Evaluates a predicate if the union holds the <see cref="All" /> type./// </summary>/// <param name="predicate">The predicate to evaluate.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext IfAll(Predicate<global::DotPointers.OneOf.All> predicate){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.All && predicate.Invoke(_source.AllForce));}/// <summary>/// Checks if the union holds the <see cref="All" /> type./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public ChainContext IfAll(){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.All);}/// <summary>/// Evaluates a predicate if the union holds the <see cref="None" /> type./// </summary>/// <param name="predicate">The predicate to evaluate.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext IfNone(Predicate<global::DotPointers.OneOf.None> predicate){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.None && predicate.Invoke(_source.NoneForce));}/// <summary>/// Checks if the union holds the <see cref="None" /> type./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public ChainContext IfNone(){if (_consumed) { return this; }return new ChainContext(_source, false, _source.Kind == OneOfRange.None);}/// <summary>/// Executes the action if the previous condition was met./// </summary>/// <param name="action">The action to execute.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Then(Action<int> action){if (_consumed || !_matched) { return this; }action.Invoke(_source.Single);return new ChainContext(_source, true, false);}/// <summary>/// Executes the action if the previous condition was met./// </summary>/// <param name="action">The action to execute.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Then(Action<(int, int)> action){if (_consumed || !_matched) { return this; }action.Invoke(_source.Between);return new ChainContext(_source, true, false);}/// <summary>/// Executes the action if the previous condition was met./// </summary>/// <param name="action">The action to execute.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Then(Action<global::DotPointers.OneOf.All> action){if (_consumed || !_matched) { return this; }action.Invoke(_source.All);return new ChainContext(_source, true, false);}/// <summary>/// Executes the action if the previous condition was met./// </summary>/// <param name="action">The action to execute.</param>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Then(Action<global::DotPointers.OneOf.None> action){if (_consumed || !_matched) { return this; }action.Invoke(_source.None);return new ChainContext(_source, true, false);}/// <summary>/// Executes the fallback action if no previous condition was met./// </summary>/// <param name="action">The fallback action to execute.</param>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly void Else(Action<Range> action){if (!_consumed) { action.Invoke(_source); }}/// <summary>/// Terminates the chain./// </summary>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly void End() { }/// <summary>/// Breaks out of the chain if the previous condition was met./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Break(){if (_consumed || !_matched) { return this; }return new ChainContext(_source, true, false);}/// <summary>/// Resets the consumed state to continue the chain evaluation./// </summary>/// <returns>The updated chain context.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public readonly ChainContext Continue(){if (_consumed) { return new ChainContext(_source, false, false); }return this;}}public OneOfMetadata Metadata => MetadataInfo.Instance;public static class MetadataInfo{public const int Count = 4;public const DotPointers.OneOf.OneOfLayoutKind Layout = DotPointers.OneOf.OneOfLayoutKind.ExplicitUnion;[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Type GetTypeAt(int index) => index switch{0 => typeof(int),1 => typeof((int, int)),2 => typeof(global::DotPointers.OneOf.All),3 => typeof(global::DotPointers.OneOf.None),_ => throw new ArgumentOutOfRangeException(nameof(index))};[MethodImpl(MethodImplOptions.AggressiveInlining)]public static string GetFieldAt(int index) => index switch{0 => "Single",1 => "Between",2 => "All",3 => "None",_ => throw new ArgumentOutOfRangeException(nameof(index))};public static readonly OneOfMetadata Instance = new OneOfMetadata(Count, Layout, Unsafe.SizeOf<Range>(),!RuntimeHelpers.IsReferenceOrContainsReferences<Range>(),GetTypeAt,GetFieldAt);}/// <summary>/// Defines the possible kinds for the Range union./// </summary>public enum OneOfRange : int{Empty = 0,Single = 1,Between = 2,All = 3,None = 4,}}public static partial class OneOfLinqExtensions{/// <summary>/// Filters a sequence of Range instances to only those holding the <see cref="Single" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> WhereSingle(this IEnumerable<Range> source) => source.Where(static x => x.IsSingle);/// <summary>/// Filters a sequence of Range instances to exclude those holding the <see cref="Single" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> ExcludeSingle(this IEnumerable<Range> source) => source.Where(static x => !x.IsSingle);/// <summary>/// Projects elements of a sequence of Range holding the <see cref="Single" /> type into a new form./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of projected values.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<int> SelectSingle(this IEnumerable<Range> source) => source.Where(static x => x.IsSingle).Select(static x => x.Single);/// <summary>/// Filters a sequence of Range instances to only those holding the <see cref="Between" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> WhereBetween(this IEnumerable<Range> source) => source.Where(static x => x.IsBetween);/// <summary>/// Filters a sequence of Range instances to exclude those holding the <see cref="Between" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> ExcludeBetween(this IEnumerable<Range> source) => source.Where(static x => !x.IsBetween);/// <summary>/// Projects elements of a sequence of Range holding the <see cref="Between" /> type into a new form./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of projected values.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<(int, int)> SelectBetween(this IEnumerable<Range> source) => source.Where(static x => x.IsBetween).Select(static x => x.Between);/// <summary>/// Filters a sequence of Range instances to only those holding the <see cref="All" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> WhereAll(this IEnumerable<Range> source) => source.Where(static x => x.IsAll);/// <summary>/// Filters a sequence of Range instances to exclude those holding the <see cref="All" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> ExcludeAll(this IEnumerable<Range> source) => source.Where(static x => !x.IsAll);/// <summary>/// Projects elements of a sequence of Range holding the <see cref="All" /> type into a new form./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of projected values.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<global::DotPointers.OneOf.All> SelectAll(this IEnumerable<Range> source) => source.Where(static x => x.IsAll).Select(static x => x.All);/// <summary>/// Filters a sequence of Range instances to only those holding the <see cref="None" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> WhereNone(this IEnumerable<Range> source) => source.Where(static x => x.IsNone);/// <summary>/// Filters a sequence of Range instances to exclude those holding the <see cref="None" /> type./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of filtered instances.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<Range> ExcludeNone(this IEnumerable<Range> source) => source.Where(static x => !x.IsNone);/// <summary>/// Projects elements of a sequence of Range holding the <see cref="None" /> type into a new form./// </summary>/// <param name="source">The source sequence.</param>/// <returns>An enumerable of projected values.</returns>[MethodImpl(MethodImplOptions.AggressiveInlining)]public static IEnumerable<global::DotPointers.OneOf.None> SelectNone(this IEnumerable<Range> source) => source.Where(static x => x.IsNone).Select(static x => x.None);}}
Вывод
В ходе статьи мы узнали как можно реализовать DU, проанализировали плюсы и минусы готовых решений, а также я создал свой вариант.
ссылка на оригинал статьи https://habr.com/ru/articles/1025098/