<|start_filename|>src/SmartEnum.AutoFixture/SmartEnumCustomization.cs<|end_filename|>
namespace Ardalis.SmartEnum.AutoFixture
{
    using global::AutoFixture;

    public class SmartEnumCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customizations.Add(new SmartEnumSpecimenBuilder());
        }
    }
}

<|start_filename|>src/SmartEnum.EFCore.UnitTests/Weekday.cs<|end_filename|>
﻿using Ardalis.SmartEnum;
using System;
using System.Collections.Generic;
using System.Text;

namespace SmartEnum.EFCore.IntegrationTests
{
    public sealed class Weekday : SmartEnum<Weekday, int>
    {
        public static readonly Weekday Monday = new Weekday(nameof(Monday), 1);
        public static readonly Weekday Tuesday = new Weekday(nameof(Tuesday), 2);
        public static readonly Weekday Wednesday = new Weekday(nameof(Wednesday), 3);
        public static readonly Weekday Thursday = new Weekday(nameof(Thursday), 4);
        public static readonly Weekday Friday = new Weekday(nameof(Friday), 5);
        public static readonly Weekday Saturday = new Weekday(nameof(Saturday), 6);
        public static readonly Weekday Sunday = new Weekday(nameof(Sunday), 7);

        private Weekday(string name, int value) : base(name, value)
        {
        }
    }
}

<|start_filename|>src/SmartEnum.AutoFixture/SmartEnumSpecimenBuilder.cs<|end_filename|>
namespace Ardalis.SmartEnum.AutoFixture
{
    using System;
    using global::AutoFixture.Kernel;

    public class SmartEnumSpecimenBuilder :
        ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            if (request is Type type && type.TryGetValues(out var enums))
            {
                // return first if not empty; otherwise fallover to NoSpecimen
                using (var enumerator = enums.GetEnumerator())
                {
                    if (enumerator.MoveNext())
                        return enumerator.Current;
                }
            }

            return new NoSpecimen();
        }
    }
}

<|start_filename|>src/SmartEnum.EFCore/SmartEnumConverterExtensions.cs<|end_filename|>
﻿using Ardalis.SmartEnum;
using Ardalis.SmartEnum.EFCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartEnum.EFCore
{
    public static class SmartEnumConverterExtensions
    {
        /// <summary>
        /// Adds a converter for all properties derived from <see cref="SmartEnum{TValue, TKey}"/>
        /// so that entity framework core can work with it.
        /// </summary>
        /// <param name="modelBuilder"></param>
        public static void ConfigureSmartEnum(this ModelBuilder modelBuilder)
        {
            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                var properties = entityType.ClrType.GetProperties()
                    .Where(p => TypeUtil.IsDerived(p.PropertyType, typeof(SmartEnum<,>)));

                foreach (var property in properties)
                {
                    var keyType = TypeUtil.GetValueType(property.PropertyType, typeof(SmartEnum<,>));

                    var converterType = typeof(SmartEnumConverter<,>).MakeGenericType(property.PropertyType, keyType);

                    var converter = (ValueConverter)Activator.CreateInstance(converterType);

                    modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion(converter);
                }
            }
        }
    }
}

<|start_filename|>test/SmartEnum.UnitTests/SmartEnumExplicitConversion.cs<|end_filename|>
﻿namespace Ardalis.SmartEnum.UnitTests
{
    using FluentAssertions;
    using Xunit;

    public class SmartEnumExplicitConversion
    {
        [Fact]
        public void ReturnsEnumFromGivenValue()
        {
            int value = 1;

            var result = (TestEnum)value;

            result.Should().BeSameAs(TestEnum.One);
        }

        [Fact]
        public void ReturnsEnumFromGivenNullableValue()
        {
            int? value = 1;

            var result = (TestEnum)value;

            result.Should().BeSameAs(TestEnum.One);
        }

        [Fact]
        public void ReturnsEnumFromGivenNullableValueAsNull()
        {
            int? value = null;

            var result = (TestEnum)value;

            result.Should().BeNull();
        }
    }
}

<|start_filename|>src/SmartEnum.ProtoBufNet/SmartEnumValueSurrogate.cs<|end_filename|>
namespace Ardalis.SmartEnum.ProtoBufNet
{
    using System;
    using ProtoBuf;

    [ProtoContract]
    public class SmartEnumValueSurrogate<TEnum, TValue>
        where TEnum : SmartEnum<TEnum, TValue>
        where TValue : struct, IEquatable<TValue>, IComparable<TValue>
    {
        [ProtoMember(1, IsRequired = true)]
        TValue Value { get; set; }

        public static implicit operator SmartEnumValueSurrogate<TEnum, TValue>(TEnum smartEnum)
        {
            if (smartEnum is null)
                return null;

            return new SmartEnumValueSurrogate<TEnum, TValue> { Value = smartEnum.Value };
        }

        public static implicit operator TEnum(SmartEnumValueSurrogate<TEnum, TValue> surrogate)
        {
            if (surrogate is null)
                return null;

            return SmartEnum<TEnum, TValue>.FromValue(surrogate.Value);
        }
    }
}

<|start_filename|>benchmarks/SmartEnum.Benchmarks/Config.cs<|end_filename|>
namespace Ardalis.SmartEnum.Benchmarks
{
    using BenchmarkDotNet.Columns;
    using BenchmarkDotNet.Configs;
    using BenchmarkDotNet.Diagnosers;
    using BenchmarkDotNet.Jobs;

    public class Config : ManualConfig
    {
        public Config()
        {
            AddJob(Job.Default);
            AddDiagnoser(MemoryDiagnoser.Default);
            AddColumn(new TagColumn("Library", name => name.Split('_')[0]));
        }
    }
}

<|start_filename|>test/SmartEnum.Utf8Json.UnitTests/SmartEnumValueFormatterTests.cs<|end_filename|>
namespace Ardalis.SmartEnum.Utf8Json.UnitTests
{
    using System;
    using System.Text;
    using global::Utf8Json;
    using global::Utf8Json.Resolvers;
    using Xunit;
    using FluentAssertions;

    public class SmartEnumValueFormatterTests
    {
        public class TestClass
        {
            [JsonFormatter(typeof(SmartEnumValueFormatter<TestEnumBoolean, bool>))]
            public TestEnumBoolean Bool { get; set; }

            [JsonFormatter(typeof(SmartEnumValueFormatter<TestEnumInt16, short>))]
            public TestEnumInt16 Int16 { get; set; }

            [JsonFormatter(typeof(SmartEnumValueFormatter<TestEnumInt32, int>))]
            public TestEnumInt32 Int32 { get; set; }

            [JsonFormatter(typeof(SmartEnumValueFormatter<TestEnumDouble, double>))]
            public TestEnumDouble Double { get; set; }
        }

        static readonly TestClass TestInstance = new TestClass
        {
            Bool = TestEnumBoolean.Instance,
            Int16 = TestEnumInt16.Instance,
            Int32 = TestEnumInt32.Instance,
            Double = TestEnumDouble.Instance,
        };

        static readonly string JsonString = @"{""Bool"":true,""Int16"":1,""Int32"":1,""Double"":1}";

        static SmartEnumValueFormatterTests()
        {
            CompositeResolver.Register(
                new SmartEnumValueFormatter<TestEnumBoolean, bool>(),
                new SmartEnumValueFormatter<TestEnumInt16, short>(),
                new SmartEnumValueFormatter<TestEnumInt32, int>(),
                new SmartEnumValueFormatter<TestEnumDouble, double>()
            );
        }

        [Fact]
        public void SerializesValue()
        {
            var json = JsonSerializer.Serialize(TestInstance);

            Encoding.UTF8.GetString(json).Should().Be(JsonString);
        }

        [Fact]
        public void DeserializesValue()
        {
            var obj = JsonSerializer.Deserialize<TestClass>(JsonString);

            obj.Bool.Should().BeSameAs(TestEnumBoolean.Instance);
            obj.Int16.Should().BeSameAs(TestEnumInt16.Instance);
            obj.Int32.Should().BeSameAs(TestEnumInt32.Instance);
            obj.Double.Should().BeSameAs(TestEnumDouble.Instance);
        }

        [Fact]
        public void DeserializesNullByDefault()
        {
            string json = @"{}";

            var obj = JsonSerializer.Deserialize<TestClass>(json);

            obj.Bool.Should().BeNull();
            obj.Int16.Should().BeNull();
            obj.Int32.Should().BeNull();
            obj.Double.Should().BeNull();
        }

        [Fact]
        public void DeserializesNull()
        {
            string json = @"{ ""Bool"": null, ""Int16"": null, ""Int32"": null, ""Double"": null }";

            var obj = JsonSerializer.Deserialize<TestClass>(json);

            obj.Bool.Should().BeNull();
            obj.Int16.Should().BeNull();
            obj.Int32.Should().BeNull();
            obj.Double.Should().BeNull();
        }

        [Fact]
        public void DeserializeThrowsWhenNotFound()
        {
            string json = @"{ ""Bool"": false }";

            Action act = () => JsonSerializer.Deserialize<TestClass>(json);

            act.Should()
                .Throw<SmartEnumNotFoundException>()
                .WithMessage($@"No {nameof(TestEnumBoolean)} with Value False found.");
        }

        public static TheoryData<string, string> NotValidData =>
            new TheoryData<string, string>
            {
                { @"{ ""Bool"": 1 }", @"expected:'true | false', actual:'1', at offset:10" },
                { @"{ ""Int16"": true }", @"expected:'Number Token', actual:'true', at offset:11" },
                { @"{ ""Int32"": true }", @"expected:'Number Token', actual:'true', at offset:11" },
                { @"{ ""Double"": true }", @"expected:'Number Token', actual:'true', at offset:12" },
            };

        [Theory]
        [MemberData(nameof(NotValidData))]
        public void DeserializeThrowsWhenNotValid(string json, string message)
        {
            Action act = () => JsonSerializer.Deserialize<TestClass>(json);

            act.Should()
                .Throw<JsonParsingException>()
                .WithMessage(message);
        }
    }
}

<|start_filename|>benchmarks/SmartEnum.Benchmarks/Program.cs<|end_filename|>
﻿namespace Ardalis.SmartEnum.Benchmarks
{
    using BenchmarkDotNet.Running;

    public static class Program
    {
        public static void Main(string[] args)
        {
            var switcher = new BenchmarkSwitcher(new[] {
                    typeof(EqualsBenchmarks),
                    typeof(SwitchBenchmarks),
                    typeof(FromValueBenchmarks),
                    typeof(FromNameBenchmarks),
                    typeof(SerializationBenchmarks),
                });
            switcher.Run(args);
        }
    }
}


<|start_filename|>benchmarks/SmartEnum.Benchmarks/FromNameBenchmarks.cs<|end_filename|>
namespace Ardalis.SmartEnum.Benchmarks
{
    using System;
    using BenchmarkDotNet.Attributes;
    using EnumsNET;

    [Config(typeof(Config))]
    public class FromNameBenchmarks
    {


        ////////////////////////////////////////////////////////////////////////////////
        // Enum

        [Benchmark]
        public TestEnum Enum_FromName_One() => Enum.Parse<TestEnum>("One");

        [Benchmark]
        public TestEnum Enum_FromName_Ten() => Enum.Parse<TestEnum>("Ten");

        [Benchmark]
        public TestEnum Enum_FromName_Invalid()
        {
            try
            {
                return Enum.Parse<TestEnum>("Invalid");
            }
            catch (Exception)
            {
                return TestEnum.One;
            }
        }

        [Benchmark]
        public TestEnum Enum_FromName_one_IgnoreCase() => Enum.Parse<TestEnum>("one", true);

        [Benchmark]
        public TestEnum Enum_FromName_ten_IgnoreCase() => Enum.Parse<TestEnum>("ten", true);


        [Benchmark]
        public TestEnum Enum_FromName_Invalid_IgnoreCase()
        {
            try
            {
                return Enum.Parse<TestEnum>("Invalid", true);
            }
            catch (Exception)
            {
                return TestEnum.One;
            }
        }

        ////////////////////////////////////////////////////////////////////////////////
        // Enums.NET

        [Benchmark]
        public TestEnum EnumsNET_FromName_One() => Enums.Parse<TestEnum>("One");

        [Benchmark]
        public TestEnum EnumsNET_FromName_Ten() => Enums.Parse<TestEnum>("Ten");

        [Benchmark]
        public TestEnum EnumsNET_FromName_Invalid()
        {
            try
            {
                return Enums.Parse<TestEnum>("Invalid");
            }
            catch (Exception)
            {
                return TestEnum.One;
            }
        }

        [Benchmark]
        public TestEnum EnumsNET_TryFromName_One()
        {
            if (Enums.TryParse<TestEnum>("One", out var result))
                return result;
            return TestEnum.One;
        }

        [Benchmark]
        public TestEnum EnumsNET_TryFromName_Ten()
        {
            if (Enums.TryParse<TestEnum>("Ten", out var result))
                return result;
            return TestEnum.One;
        }

        [Benchmark]
        public TestEnum EnumsNET_TryFromName_Invalid()
        {
            if (Enums.TryParse<TestEnum>("Invalid", out var result))
                return result;
            return TestEnum.One;
        }

        ////////////////////////////////////////////////////////////////////////////////
        // Constant

        [Benchmark]
        public TestConstant Constant_GetFor_One() => global::Constant.Constant<string, TestConstant>.GetFor("One");

        [Benchmark]
        public TestConstant Constant_GetFor_Ten() => global::Constant.Constant<string, TestConstant>.GetFor("Ten");

        [Benchmark]
        public TestConstant Constant_GetFor_Invalid()
        {
            try
            {
                return global::Constant.Constant<string, TestConstant>.GetFor("Invalid");
            }
            catch (Exception)
            {
                return TestConstant.One;
            }
        }

        [Benchmark]
        public TestConstant Constant_GetOrDefaultFor_One() => global::Constant.Constant<string, TestConstant>.GetOrDefaultFor("One");

        [Benchmark]
        public TestConstant Constant_GetOrDefaultFor_Ten() => global::Constant.Constant<string, TestConstant>.GetOrDefaultFor("Ten");

        [Benchmark]
        public TestConstant Constant_GetOrDefaultFor_Invalid()
        {
            try
            {
                return global::Constant.Constant<string, TestConstant>.GetOrDefaultFor("Invalid");
            }
            catch (Exception)
            {
                return TestConstant.One;
            }
        }

        ////////////////////////////////////////////////////////////////////////////////
        // SmartEnum

        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_One() => TestSmartEnum.FromName("One", false);

        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_Ten() => TestSmartEnum.FromName("Ten", false);

        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_Invalid()
        {
            try
            {
                return TestSmartEnum.FromName("Invalid", false);
            }
            catch (SmartEnumNotFoundException)
            {
                return null;
            }
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_one_IgnoreCase() => TestSmartEnum.FromName("one", true);

        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_ten_IgnoreCase() => TestSmartEnum.FromName("ten", true);


        [Benchmark]
        public TestSmartEnum SmartEnum_FromName_Invalid_IgnoreCase()
        {
            try
            {
                return TestSmartEnum.FromName("Invalid", true);
            }
            catch (SmartEnumNotFoundException)
            {
                return null;
            }
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_One()
        {
            if (TestSmartEnum.TryFromName("One", false, out var result))
                return result;
            return null;
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_Ten()
        {
            if (TestSmartEnum.TryFromName("Ten", false, out var result))
                return result;
            return null;
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_Invalid()
        {
            if (TestSmartEnum.TryFromName("Invalid", false, out var result))
                return result;
            return null;
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_one_IgnoreCase()
        {
            if (TestSmartEnum.TryFromName("one", true, out var result))
                return result;
            return null;
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_ten_IgnoreCase()
        {
            if (TestSmartEnum.TryFromName("ten", true, out var result))
                return result;
            return null;
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_TryFromName_Invalid_IgnoreCase()
        {
            if (TestSmartEnum.TryFromName("Invalid", true, out var result))
                return result;
            return null;
        }

    }
}

<|start_filename|>test/SmartEnum.UnitTests/SmartEnumStringEquals.cs<|end_filename|>
namespace Ardalis.SmartEnum.UnitTests
{
    using FluentAssertions;
    using Xunit;

    public class SmartEnumStringEquals
    {
        public static TheoryData<TestStringEnum, object, bool> EqualsTestEnumObjectData =>
            new TheoryData<TestStringEnum, object, bool>
            {
                { TestStringEnum.One, null, false },
                { TestStringEnum.One, TestEnum.One, false },
                { TestStringEnum.One, TestStringEnum.One, true },
                { TestStringEnum.One, TestStringEnum.Two, false },
            };

        [Theory]
        [MemberData(nameof(EqualsTestEnumObjectData))]
        public void EqualsObjectReturnsExpected(TestStringEnum left, object right, bool expected)
        {
            var result = left.Equals(right);

            result.Should().Be(expected);
        }

        public static TheoryData<TestStringEnum, TestStringEnum, bool> EqualsSmartEnumData =>
            new TheoryData<TestStringEnum, TestStringEnum, bool>
            {
                { TestStringEnum.One, null, false },
                { TestStringEnum.One, TestStringEnum.One, true },
                { TestStringEnum.One, TestStringEnum.Two, false },
            };

        [Theory]
        [MemberData(nameof(EqualsSmartEnumData))]
        public void EqualsSmartEnumReturnsExpected(TestStringEnum left, TestStringEnum right, bool expected)
        {
            var result = left.Equals(right);

            result.Should().Be(expected);
        }

        public static TheoryData<TestStringEnum, TestStringEnum, bool> EqualOperatorData =>
            new TheoryData<TestStringEnum, TestStringEnum, bool>
            {
                { null, null, true },
                { null, TestStringEnum.One, false },
                { TestStringEnum.One, null, false },
                { TestStringEnum.One, TestStringEnum.One, true },
                { TestStringEnum.One, TestStringEnum.Two, false },
            };

        [Theory]
        [MemberData(nameof(EqualOperatorData))]
        public void EqualOperatorReturnsExpected(TestStringEnum left, TestStringEnum right, bool expected)
        {
            var result = left == right;

            result.Should().Be(expected);
        }

        [Theory]
        [MemberData(nameof(EqualOperatorData))]
        public void NotEqualOperatorReturnsExpected(TestStringEnum left, TestStringEnum right, bool expected)
        {
            var result = left != right;

            result.Should().Be(!expected);
        }
    }
}

<|start_filename|>src/SmartEnum.SystemTextJson/SmartEnumNameConverter.cs<|end_filename|>
namespace Ardalis.SmartEnum.SystemTextJson
{
    using System;
    using System.Text.Json;
    using System.Text.Json.Serialization;

    public class SmartEnumNameConverter<TEnum, TValue> : JsonConverter<TEnum>
        where TEnum : SmartEnum<TEnum, TValue>
        where TValue : IEquatable<TValue>, IComparable<TValue>, IConvertible
    {
        public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            switch (reader.TokenType)
            {
                case JsonTokenType.String:
                    return GetFromName(reader.GetString());

                default:
                    throw new JsonException($"Unexpected token {reader.TokenType} when parsing a smart enum.");
            }
        }

        public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
        {
            if (value == null)
                writer.WriteNullValue();
            else
                writer.WriteStringValue(value.Name.ToString());
        }

        private TEnum GetFromName(string name)
        {
            try
            {
                return SmartEnum<TEnum, TValue>.FromName(name, false);
            }
            catch (Exception ex)
            {
                throw new JsonException($"Error converting value '{name}' to a smart enum.", ex);
            }
        }
    }
}

<|start_filename|>test/SmartEnum.JsonNet.UnitTests/TestEnums.cs<|end_filename|>
﻿namespace Ardalis.SmartEnum.JsonNet.UnitTests
{
    public sealed class TestEnumBoolean : SmartEnum<TestEnumBoolean, bool>
    {
        public static readonly TestEnumBoolean Instance = new TestEnumBoolean(nameof(Instance), true);

        TestEnumBoolean(string name, bool value) : base(name, value) { }
    }

    public sealed class TestEnumInt16 : SmartEnum<TestEnumInt16, short>
    {
        public static readonly TestEnumInt16 Instance = new TestEnumInt16(nameof(Instance), 1);

        TestEnumInt16(string name, short value) : base(name, value) { }
    }

    public sealed class TestEnumInt32 : SmartEnum<TestEnumInt32, int>
    {
        public static readonly TestEnumInt32 Instance = new TestEnumInt32(nameof(Instance), 1);

        TestEnumInt32(string name, int value) : base(name, value) { }
    }

    public sealed class TestEnumDouble : SmartEnum<TestEnumDouble, double>
    {
        public static readonly TestEnumDouble Instance = new TestEnumDouble(nameof(Instance), 1.0);

        TestEnumDouble(string name, double value) : base(name, value) { }
    }

    public sealed class TestEnumString : SmartEnum<TestEnumString, string>
    {
        public static readonly TestEnumString Instance = new TestEnumString(nameof(Instance), "string");

        TestEnumString(string name, string value) : base(name, value) { }
    }
}


<|start_filename|>benchmarks/SmartEnum.Benchmarks/SwitchBenchmarks.cs<|end_filename|>
namespace Ardalis.SmartEnum.Benchmarks
{
    using System;
    using BenchmarkDotNet.Attributes;

    [Config(typeof(Config))]
    public class SwitchBenchmarks
    {
        ////////////////////////////////////////////////////////////////////////////////
        // Enum

        [Benchmark]
        public TestEnum Enum_Switch()
        {
            var value = TestEnum.Ten;
            switch (value)
            {
                case TestEnum.One:
                    return TestEnum.One;
                case TestEnum.Two:
                    return TestEnum.Two;
                case TestEnum.Three:
                    return TestEnum.Three;
                case TestEnum.Four:
                    return TestEnum.Four;
                case TestEnum.Five:
                    return TestEnum.Five;
                case TestEnum.Six:
                    return TestEnum.Six;
                case TestEnum.Seven:
                    return TestEnum.Seven;
                case TestEnum.Eight:
                    return TestEnum.Eight;
                case TestEnum.Nine:
                    return TestEnum.Nine;
                case TestEnum.Ten:
                    return TestEnum.Ten;
                default: throw new Exception();
            }
        }

        [Benchmark]
        public TestEnum Enum_IfEqualsMethodCascade()
        {
            var value = TestEnum.Ten;
            if (value.Equals(TestEnum.One))
                return TestEnum.One;
            if (value.Equals(TestEnum.Two))
                return TestEnum.Two;
            if (value.Equals(TestEnum.Three))
                return TestEnum.Three;
            if (value.Equals(TestEnum.Five))
                return TestEnum.Five;
            if (value.Equals(TestEnum.Six))
                return TestEnum.Six;
            if (value.Equals(TestEnum.Seven))
                return TestEnum.Seven;
            if (value.Equals(TestEnum.Eight))
                return TestEnum.Eight;
            if (value.Equals(TestEnum.Nine))
                return TestEnum.Nine;
            if (value.Equals(TestEnum.Ten))
                return TestEnum.Ten;
            throw new Exception();
        }

        [Benchmark]
        public TestEnum Enum_IfEqualOperatorCascade()
        {
            var value = TestEnum.Ten;
            if (value == TestEnum.One)
                return TestEnum.One;
            if (value == TestEnum.Two)
                return TestEnum.Two;
            if (value == TestEnum.Three)
                return TestEnum.Three;
            if (value == TestEnum.Five)
                return TestEnum.Five;
            if (value == TestEnum.Six)
                return TestEnum.Six;
            if (value == TestEnum.Seven)
                return TestEnum.Seven;
            if (value == TestEnum.Eight)
                return TestEnum.Eight;
            if (value == TestEnum.Nine)
                return TestEnum.Nine;
            if (value == TestEnum.Ten)
                return TestEnum.Ten;
            throw new Exception();
        }

        ////////////////////////////////////////////////////////////////////////////////
        // SmartEnum

        [Benchmark]
        public TestSmartEnum SmartEnum_Switch()
        {
            var value = TestSmartEnum.Ten;
            switch (value.Name)
            {
                case nameof(TestSmartEnum.One):
                    return TestSmartEnum.One;
                case nameof(TestSmartEnum.Two):
                    return TestSmartEnum.Two;
                case nameof(TestSmartEnum.Three):
                    return TestSmartEnum.Three;
                case nameof(TestSmartEnum.Four):
                    return TestSmartEnum.Four;
                case nameof(TestSmartEnum.Five):
                    return TestSmartEnum.Five;
                case nameof(TestSmartEnum.Six):
                    return TestSmartEnum.Six;
                case nameof(TestSmartEnum.Seven):
                    return TestSmartEnum.Seven;
                case nameof(TestSmartEnum.Eight):
                    return TestSmartEnum.Eight;
                case nameof(TestSmartEnum.Nine):
                    return TestSmartEnum.Nine;
                case nameof(TestSmartEnum.Ten):
                    return TestSmartEnum.Ten;
                default:
                    throw new Exception();
            }
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_SwitchPatternMatching()
        {
            var value = TestSmartEnum.Ten;
            switch (value)
            {
                case var smartEnum when smartEnum.Equals(TestSmartEnum.One):
                    return TestSmartEnum.One;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Two):
                    return TestSmartEnum.Two;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Three):
                    return TestSmartEnum.Three;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Four):
                    return TestSmartEnum.Four;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Five):
                    return TestSmartEnum.Five;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Six):
                    return TestSmartEnum.Six;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Seven):
                    return TestSmartEnum.Seven;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Eight):
                    return TestSmartEnum.Eight;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Nine):
                    return TestSmartEnum.Nine;
                case var smartEnum when smartEnum.Equals(TestSmartEnum.Ten):
                    return TestSmartEnum.Ten;
                default:
                    throw new Exception();
            }
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_IfCascade()
        {
            var value = TestSmartEnum.Ten;
            if (value.Equals(TestSmartEnum.One))
                return TestSmartEnum.One;
            if (value.Equals(TestSmartEnum.Two))
                return TestSmartEnum.Two;
            if (value.Equals(TestSmartEnum.Three))
                return TestSmartEnum.Three;
            if (value.Equals(TestSmartEnum.Five))
                return TestSmartEnum.Five;
            if (value.Equals(TestSmartEnum.Six))
                return TestSmartEnum.Six;
            if (value.Equals(TestSmartEnum.Seven))
                return TestSmartEnum.Seven;
            if (value.Equals(TestSmartEnum.Eight))
                return TestSmartEnum.Eight;
            if (value.Equals(TestSmartEnum.Nine))
                return TestSmartEnum.Nine;
            if (value.Equals(TestSmartEnum.Ten))
                return TestSmartEnum.Ten;
            throw new Exception();
        }

        [Benchmark]
        public TestSmartEnum SmartEnum_When()
        {
            TestSmartEnum result = null;

            TestSmartEnum.Ten
                .When(TestSmartEnum.One).Then(() => result = TestSmartEnum.One)
                .When(TestSmartEnum.Two).Then(() => result = TestSmartEnum.Two)
                .When(TestSmartEnum.Three).Then(() => result = TestSmartEnum.Three)
                .When(TestSmartEnum.Four).Then(() => result = TestSmartEnum.Four)
                .When(TestSmartEnum.Five).Then(() => result = TestSmartEnum.Five)
                .When(TestSmartEnum.Six).Then(() => result = TestSmartEnum.Six)
                .When(TestSmartEnum.Seven).Then(() => result = TestSmartEnum.Seven)
                .When(TestSmartEnum.Eight).Then(() => result = TestSmartEnum.Eight)
                .When(TestSmartEnum.Nine).Then(() => result = TestSmartEnum.Nine)
                .When(TestSmartEnum.Ten).Then(() => result = TestSmartEnum.Ten)
                .Default(() => throw new Exception());

            return result;
        }
    }
}

<|start_filename|>src/SmartEnum.Utf8Json/SmartEnumValueFormatter.cs<|end_filename|>
namespace Ardalis.SmartEnum.Utf8Json
{
    using global::Utf8Json;
    using System;

    public class SmartEnumValueFormatter<TEnum, TValue> : IJsonFormatter<TEnum>
        where TEnum : SmartEnum<TEnum, TValue>
        where TValue : struct, IEquatable<TValue>, IComparable<TValue>
    {
        public void Serialize(ref JsonWriter writer, TEnum value, IJsonFormatterResolver formatterResolver)
        {
            if (value is null)
            {
                writer.WriteNull();
                return;
            }

            if (typeof(TValue) == typeof(bool))
                writer.WriteBoolean((bool)(object)value.Value);
            else if (typeof(TValue) == typeof(byte))
                writer.WriteByte((byte)(object)value.Value);
            else if (typeof(TValue) == typeof(sbyte))
                writer.WriteSByte((sbyte)(object)value.Value);
            else if (typeof(TValue) == typeof(short))
                writer.WriteInt16((short)(object)value.Value);
            else if (typeof(TValue) == typeof(ushort))
                writer.WriteUInt16((ushort)(object)value.Value);
            else if (typeof(TValue) == typeof(int))
                writer.WriteInt32((int)(object)value.Value);
            else if (typeof(TValue) == typeof(uint))
                writer.WriteUInt32((uint)(object)value.Value);
            else if (typeof(TValue) == typeof(long))
                writer.WriteInt64((long)(object)value.Value);
            else if (typeof(TValue) == typeof(ulong))
                writer.WriteUInt64((ulong)(object)value.Value);
            else if (typeof(TValue) == typeof(float))
                writer.WriteSingle((float)(object)value.Value);
            else if (typeof(TValue) == typeof(double))
                writer.WriteDouble((double)(object)value.Value);
            else
                throw new ArgumentOutOfRangeException(typeof(TValue).ToString(), $"{typeof(TValue).Name} is not supported.");
        }

        public TEnum Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
        {
            if (reader.ReadIsNull())
                return null;

            return SmartEnum<TEnum, TValue>.FromValue(ReadValue(ref reader));
        }

        TValue ReadValue(ref JsonReader reader)
        {
            if (typeof(TValue) == typeof(bool))
                return (TValue)(object)reader.ReadBoolean();
            if (typeof(TValue) == typeof(byte))
                return (TValue)(object)reader.ReadByte();
            if (typeof(TValue) == typeof(sbyte))
                return (TValue)(object)reader.ReadSByte();
            if (typeof(TValue) == typeof(short))
                return (TValue)(object)reader.ReadInt16();
            if (typeof(TValue) == typeof(ushort))
                return (TValue)(object)reader.ReadUInt16();
            if (typeof(TValue) == typeof(int))
                return (TValue)(object)reader.ReadInt32();
            if (typeof(TValue) == typeof(uint))
                return (TValue)(object)reader.ReadUInt32();
            if (typeof(TValue) == typeof(long))
                return (TValue)(object)reader.ReadInt64();
            if (typeof(TValue) == typeof(ulong))
                return (TValue)(object)reader.ReadUInt64();
            if (typeof(TValue) == typeof(float))
                return (TValue)(object)reader.ReadSingle();
            if (typeof(TValue) == typeof(double))
                return (TValue)(object)reader.ReadDouble();
            throw new ArgumentOutOfRangeException(typeof(TValue).ToString(), $"{typeof(TValue).Name} is not supported.");
        }
    }
}

<|start_filename|>src/SmartEnum.EFCore.UnitTests/DbContext/Entities/SomeEntity.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SmartEnum.EFCore.IntegrationTests.DbContext.Entities
{
    public class SomeEntity
    {
        public int Id { get; set; }

        public Weekday Weekday { get; set; }
    }
}

<|start_filename|>src/SmartEnum.EFCore/SmartEnumConverter.cs<|end_filename|>
﻿using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SmartEnum.EFCore;
using System;
using System.Reflection;

namespace Ardalis.SmartEnum.EFCore
{
    public class SmartEnumConverter<TEnum, TValue> : ValueConverter<TEnum, TValue>
        where TEnum : SmartEnum<TEnum, TValue>
        where TValue : IEquatable<TValue>, IComparable<TValue>
    {
        private static bool CanConvert(Type objectType)
        {
            return TypeUtil.IsDerived(objectType, typeof(SmartEnum<,>));
        }

        private static MethodInfo GetBaseFromValueMethod(Type objectType, Type valueType)
        {
            Type currentType = objectType.BaseType;

            if (currentType == null)
            {
                return null;
            }

            while (currentType != typeof(object))
            {
                if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == typeof(SmartEnum<,>))
                    return currentType.GetMethod("FromValue", new Type[] { valueType });

                currentType = currentType.BaseType;
            }

            return null;
        }

        public static TEnum GetFromValue(TValue value)
        {
            if (!CanConvert(typeof(TEnum)))
            {
                throw new NotImplementedException();
            }

            var method = GetBaseFromValueMethod(typeof(TEnum), typeof(TValue));

            return method.Invoke(null, new[] { (object)value }) as TEnum;
        }

        public SmartEnumConverter() : base(item => item.Value, key => GetFromValue(key), null)
        {
        }
    }
}

<|start_filename|>test/SmartEnum.UnitTests/SmartEnumFromValue.cs<|end_filename|>
﻿namespace Ardalis.SmartEnum.UnitTests
{
    using System;
    using FluentAssertions;
    using Xunit;

    public class SmartEnumFromValue
    {
        [Fact]
        public void ReturnsEnumGivenMatchingValue()
        {
            var result = TestEnum.FromValue(1);

            result.Should().BeSameAs(TestEnum.One);
        }

        [Fact]
        public void ReturnsEnumGivenDerivedClass()
        {
            var result = TestDerivedEnum.FromValue(1);

            result.Should().NotBeNull().And.BeSameAs(TestDerivedEnum.One);
        }

        [Fact]
        public void ThrowsGivenNonMatchingValue()
        {
            var value = -1;

            Action action = () => TestEnum.FromValue(value);

            action.Should()
            .ThrowExactly<SmartEnumNotFoundException>()
            .WithMessage($"No {typeof(TestEnum).Name} with Value {value} found.");

        }

        [Fact]
        public void ReturnsDefaultEnumGivenNonMatchingValue()
        {
            var value = -1;
            var defaultEnum = TestEnum.One;

            var result = TestEnum.FromValue(value, defaultEnum);

            result.Should().BeSameAs(defaultEnum);
        }
    }
}

<|start_filename|>src/SmartEnum.EFCore.UnitTests/DbContext/TestDbContext.cs<|end_filename|>
﻿using Microsoft.EntityFrameworkCore;
using SmartEnum.EFCore.IntegrationTests.DbContext.Entities;
using System;
using System.Collections.Generic;
using System.Text;

namespace SmartEnum.EFCore.IntegrationTests.DbContext
{
    public class TestDbContext : Microsoft.EntityFrameworkCore.DbContext
    {
        public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
        { }

        public TestDbContext()
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
                optionsBuilder.UseSqlite("Data Source=:memory:;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ConfigureSmartEnum();
        }

        public DbSet<SomeEntity> SomeEntities { get; set; }
    }
}

<|start_filename|>src/SmartEnum.EFCore.UnitTests/SmartEnumEFCoreDbTests.cs<|end_filename|>
using FluentAssertions;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using SmartEnum.EFCore.IntegrationTests.DbContext;
using SmartEnum.EFCore.IntegrationTests.DbContext.Entities;
using System;
using System.Linq;
using Xunit;

namespace SmartEnum.EFCore.IntegrationTests
{
    public class SmartEnumEFCoreDbTests
    {
        [Fact]
        public void Test_SaveAndRetrieveData()
        {
            // In-memory database only exists while the connection is open
            using (var connection = new SqliteConnection("DataSource=:memory:"))
            {
                connection.Open();

                var options = new DbContextOptionsBuilder<TestDbContext>()
                    .UseSqlite(connection)
                    .Options;

                using (var db = new TestDbContext(options))
                {
                    db.Database.EnsureCreated();

                    db.SomeEntities.Add(new SomeEntity
                    {
                        Weekday = Weekday.Thursday,
                    });

                    db.SaveChanges();
                }

                using (var db = new TestDbContext(options))
                {
                    var entities = db.SomeEntities.ToList();

                    entities.Count.Should().Be(1);
                    entities.Single().Weekday.Should().Be(Weekday.Thursday);
                }
            }
        }
    }
}

<|start_filename|>test/SmartEnum.UnitTests/SmartEnumTryFromName.cs<|end_filename|>
namespace Ardalis.SmartEnum.UnitTests
{
    using System;
    using FluentAssertions;
    using Xunit;

    public class SmartEnumTryFromName
    {
        [Fact]
        public void ReturnsTrueGivenMatchingName()
        {
            var result = TestEnum.TryFromName("One", out var _);

            result.Should().BeTrue();
        }

        [Fact]
        public void ProducesEnumGivenMatchingName()
        {
            TestEnum.TryFromName("One", out var result);

            result.Should().BeSameAs(TestEnum.One);
        }

        [Fact]
        public void ReturnsFalseGivenEmptyString()
        {
            var result = TestEnum.TryFromName(String.Empty, out var _);

            result.Should().BeFalse();
        }

        [Fact]
        public void ProducesNullGivenEmptyString()
        {
            TestEnum.TryFromName(String.Empty, out var result);

            result.Should().BeNull();
        }

        [Fact]
        public void ReturnsFalseGivenNull()
        {
            var result = TestEnum.TryFromName(null, out var _);

            result.Should().BeFalse();
        }

        [Fact]
        public void ProducesNullGivenNull()
        {
            TestEnum.TryFromName(null, out var result);

            result.Should().BeNull();
        }
    }
}

<|start_filename|>src/SmartEnum.EFCore.UnitTests/Migrations/20190622162355_Initial.Designer.cs<|end_filename|>
﻿// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SmartEnum.EFCore.IntegrationTests.DbContext;

namespace SmartEnum.EFCore.IntegrationTests.Migrations
{
    [DbContext(typeof(TestDbContext))]
    [Migration("20190622162355_Initial")]
    partial class Initial
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.2.4-servicing-10062");

            modelBuilder.Entity("SmartEnum.EFCore.IntegrationTests.DbContext.Entities.SomeEntity", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<int?>("Weekday");

                    b.HasKey("Id");

                    b.ToTable("SomeEntities");
                });
#pragma warning restore 612, 618
        }
    }
}


<|start_filename|>test/SmartEnum.UnitTests/SmartEnumExtensionsTests.cs<|end_filename|>
namespace Ardalis.SmartEnum.UnitTests
{
    using System;
    using FluentAssertions;
    using Xunit;

    public class SmartEnumExtensionsTests
    {
        public abstract class AbstractEnum : SmartEnum<AbstractEnum, int>
        {
            protected AbstractEnum(string name, int value) : base(name, value) { }
        }

        public class GenericEnum<T> :
            SmartEnum<GenericEnum<T>, T>
            where T : struct, IEquatable<T>, IComparable<T>
        {
            protected GenericEnum(string name, T value) : base(name, value) { }
        }

        public static TheoryData<Type, bool, Type[]> IsSmartEnumData =>
            new TheoryData<Type, bool, Type[]>
            {
                { typeof(int), false, null },
                { typeof(AbstractEnum), false, null },
                { typeof(GenericEnum<>), false, null },
                { typeof(TestEnum), true, new Type[] { typeof(TestEnum), typeof(int) }},
            };

        [Theory]
        [MemberData(nameof(IsSmartEnumData))]
#pragma warning disable xUnit1026 // Theory methods should use all of their parameters
        public void IsSmartEnumReturnsExpected(Type type, bool expectedResult, Type[] _)
#pragma warning restore xUnit1026 // Theory methods should use all of their parameters
        {
            var result = type.IsSmartEnum();

            result.Should().Be(expectedResult);
        }

        [Theory]
        [MemberData(nameof(IsSmartEnumData))]
        public void IsSmartEnum2ReturnsExpected(Type type, bool expectedResult, Type[] expectedGenericArguments)
        {
            var result = type.IsSmartEnum(out var genericArguments);

            result.Should().Be(expectedResult);
            if (result)
            {
                genericArguments.Should().Equal(expectedGenericArguments);
            }
        }
    }
}


<|start_filename|>src/SmartEnum.SystemTextJson/SmartEnumValueConverter.cs<|end_filename|>
namespace Ardalis.SmartEnum.SystemTextJson
{
    using System;
    using System.Text.Json;
    using System.Text.Json.Serialization;

    public class SmartEnumValueConverter<TEnum, TValue> : JsonConverter<TEnum>
        where TEnum : SmartEnum<TEnum, TValue>
        where TValue : IEquatable<TValue>, IComparable<TValue>, IConvertible
    {
        public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.Null)
                return null;

            return GetFromValue(ReadValue(ref reader));
        }

        public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
        {
            if (value == null)
                writer.WriteNullValue();
            else if (typeof(TValue) == typeof(bool))
                writer.WriteBooleanValue((bool)(object)value.Value);
            else if (typeof(TValue) == typeof(short))
                writer.WriteNumberValue((int)(short)(object)value.Value);
            else if (typeof(TValue) == typeof(int))
                writer.WriteNumberValue((int)(object)value.Value);
            else if (typeof(TValue) == typeof(double))
                writer.WriteNumberValue((double)(object)value.Value);
            else if (typeof(TValue) == typeof(decimal))
                writer.WriteNumberValue((decimal)(object)value.Value);
            else if (typeof(TValue) == typeof(ulong))
                writer.WriteNumberValue((ulong)(object)value.Value);
            else if (typeof(TValue) == typeof(uint))
                writer.WriteNumberValue((uint)(object)value.Value);
            else if (typeof(TValue) == typeof(float))
                writer.WriteNumberValue((float)(object)value.Value);
            else if (typeof(TValue) == typeof(long))
                writer.WriteNumberValue((long)(object)value.Value);
            else
                writer.WriteStringValue(value.Value.ToString());
        }

        private TEnum GetFromValue(TValue value)
        {
            try
            {
                return SmartEnum<TEnum, TValue>.FromValue(value);
            }
            catch (Exception ex)
            {
                throw new JsonException($"Error converting value '{value}' to a smart enum.", ex);
            }
        }

        private TValue ReadValue(ref Utf8JsonReader reader)
        {
            if (typeof(TValue) == typeof(bool))
                return (TValue)(object)reader.GetBoolean();
            if (typeof(TValue) == typeof(byte))
                return (TValue)(object)reader.GetByte();
            if (typeof(TValue) == typeof(sbyte))
                return (TValue)(object)reader.GetSByte();
            if (typeof(TValue) == typeof(short))
                return (TValue)(object)reader.GetInt16();
            if (typeof(TValue) == typeof(ushort))
                return (TValue)(object)reader.GetUInt16();
            if (typeof(TValue) == typeof(int))
                return (TValue)(object)reader.GetInt32();
            if (typeof(TValue) == typeof(uint))
                return (TValue)(object)reader.GetUInt32();
            if (typeof(TValue) == typeof(long))
                return (TValue)(object)reader.GetInt64();
            if (typeof(TValue) == typeof(ulong))
                return (TValue)(object)reader.GetUInt64();
            if (typeof(TValue) == typeof(float))
                return (TValue)(object)reader.GetSingle();
            if (typeof(TValue) == typeof(double))
                return (TValue)(object)reader.GetDouble();
            if (typeof(TValue) == typeof(string))
                return (TValue)(object)reader.GetString();

            throw new ArgumentOutOfRangeException(typeof(TValue).ToString(), $"{typeof(TValue).Name} is not supported.");
        }
    }
}