Skip to content

Instantly share code, notes, and snippets.

@cgbeutler
Last active May 1, 2019 16:47
Show Gist options
  • Save cgbeutler/06583045b7e19ab9dc089b16452ed34f to your computer and use it in GitHub Desktop.
Save cgbeutler/06583045b7e19ab9dc089b16452ed34f to your computer and use it in GitHub Desktop.
String Enumeration class for C#
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