Last active
May 1, 2019 16:47
-
-
Save cgbeutler/06583045b7e19ab9dc089b16452ed34f to your computer and use it in GitHub Desktop.
String Enumeration class for C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Newtonsoft.Json; | |
public sealed class ExampleEnum : StringEnumeration<ExampleEnum> | |
{ | |
public static readonly ExampleEnum Value1 = new ExampleEnum(nameof(Value1)); | |
public static readonly ExampleEnum Value2 = new ExampleEnum(nameof(Value2)); | |
public static readonly ExampleEnum Value3 = new ExampleEnum(nameof(Value3)); | |
private ExampleEnum(string value) : base(value) {} | |
} | |
// Just used for the JsonConverter below | |
internal interface IStringEnumeration | |
{ | |
string Value { get; } | |
} | |
/// <summary> | |
/// String Enumeration class with fast case-ignored parsing and json representation as a string. | |
/// </summary> | |
/// <typeparam name="TEnum">The derived type that inherits from this base class (CRTP)</typeparam> | |
[JsonConverter(typeof(StringEnumerationConverter))] | |
public abstract class StringEnumeration<TEnum> : IStringEnumeration, IComparable | |
where TEnum : StringEnumeration<TEnum> | |
{ | |
private static readonly Dictionary<string, StringEnumeration<TEnum>> ParseMapping = new Dictionary<string, StringEnumeration<TEnum>>(StringComparer.OrdinalIgnoreCase); | |
public string Value { get; } | |
static StringEnumeration() | |
{ | |
// Ensure that we set up the derived class's enum members to fill the "ParseMapping" dictionary | |
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle); | |
} | |
protected StringEnumeration(string enumValue) | |
{ | |
Value = enumValue; | |
ParseMapping.Add(enumValue, this); | |
} | |
public static TEnum Parse(string value) | |
{ | |
if (ParseMapping.TryGetValue(value.ToLower(), out var result)) | |
{ | |
return (TEnum)result; | |
} | |
throw new InvalidCastException(); | |
} | |
public static IEnumerable<TEnum> All => ParseMapping.Values.AsEnumerable().Cast<TEnum>(); | |
public override string ToString() { return Value; } | |
public override bool Equals(object obj) | |
{ | |
if (!(obj is TEnum otherValue)) | |
return false; | |
return Value.Equals(otherValue.Value); | |
} | |
public int CompareTo(object other) => StringComparer.OrdinalIgnoreCase.Compare(Value, ((TEnum)other).Value); | |
public override int GetHashCode() | |
{ | |
return 2108858624 + Value.GetHashCode(); | |
} | |
} | |
public class StringEnumerationConverter : JsonConverter | |
{ | |
public override bool CanConvert(Type objectType) | |
{ | |
return typeof(string).IsAssignableFrom(objectType) || typeof(StringEnumeration<>).IsAssignableFrom(objectType); | |
} | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |
{ | |
var stringEnum = value as IStringEnumeration; | |
writer.WriteValue(stringEnum?.Value); | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |
{ | |
if (reader.TokenType == JsonToken.String) | |
{ | |
var method = objectType.BaseType.GetMethod("Parse"); | |
return method.Invoke(null, new object[] { (string)reader.Value }); | |
} | |
throw new JsonSerializationException("Expected a string"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment