Skip to content

Instantly share code, notes, and snippets.

@JohnLBevan
Last active March 7, 2018 12:33
Show Gist options
  • Select an option

  • Save JohnLBevan/0c1676b2287255686402d680c399f87b to your computer and use it in GitHub Desktop.

Select an option

Save JohnLBevan/0c1676b2287255686402d680c399f87b to your computer and use it in GitHub Desktop.
Futureproof enum classes
/*
A while back I started using classes with static properties to represent enums
More on that here: https://stackoverflow.com/a/15713789/361842
This is the beginning of a reusable base class to allow me to easily create
these pseudo-enums without having to repeat myself each time.
Not yet complete; once done I'll add another file to this gist to represent a couple of use cases...
*/
public abstract class AdvancedEnum
{
#region attributes
static AdvancedEnumCollection collection = new AdvancedEnumCollection();
#endregion attributes
#region Properties
private readonly int id;
public int Id { get { return id; } }
private readonly string name;
public string Name { get { return name; } }
private readonly string displayName;
public string DisplayName { get { return displayName;} }
#endregion Properties
#region Constructors
protected AdvancedEnum(int id, string name) : this(id, name, name) { }
protected AdvancedEnum(int id, string name, string displayName)
{
this.id = id;
this.name = name;
this.displayName = displayName;
collection.Add(this);
}
#endregion Constructors
#region Overridden Methods
public override bool Equals(object obj)
{
return Equals(obj as AdvancedEnum);
}
public bool Equals(AdvancedEnum obj)
{
return obj != null
&& id.Equals(obj.id)
&& name.Equals(obj.name)
&& displayName.Equals(obj.DisplayName);
}
#endregion Overridden Methods
}
public class AdvancedEnumCollection: ICollection<AdvancedEnum>
{
static object syncLock = new object();
IDictionary<int, AdvancedEnum> allValuesById = new Dictionary<int, AdvancedEnum>();
public int Count => allValuesById.Count();
public bool IsReadOnly => false; //we want to make it read only once fully populated; but for now just leave as false
public void Add(AdvancedEnum item)
{
if (AssertIdIsUnique(item)) //if the item is not unique error straight away
{
lock (syncLock) //if it was unique lock the collection, then recheck in more detail that it's still unique / that other properties are also unique
{
AssertIdIsUnique(item);
AssertNameIsUnique(item);
AssertDisplayNameIsUnique(item);
//if confirmed that all three properties are unique (i.e. don't already exist) then add the item to the collection
allValuesById.Add(item.Id, item);
}
}
}
private bool AssertIdIsUnique(AdvancedEnum item)
{
if (allValuesById.ContainsKey(item.Id))
throw new DuplicateException<int>(item.Id, "Id");
return true;
}
private bool AssertNameIsUnique(AdvancedEnum item)
{
if (allValuesById.Any(x => x.Value.Name.Equals(item.Name, StringComparison.Ordinal)))
throw new DuplicateException<string>(item.Name, "Name");
return true;
}
private bool AssertDisplayNameIsUnique(AdvancedEnum item)
{
if (allValuesById.Any(x => x.Value.DisplayName.Equals(item.DisplayName,StringComparison.Ordinal)))
throw new DuplicateException<string>(item.Name, "DisplayName");
return true;
}
public void Clear()
{
//values don't change after population; there's no good reason to allow this method
throw new InvalidOperationException();
}
public bool Contains(AdvancedEnum item)
{
return allValuesById.ContainsKey(item.Id)
&& allValuesById[item.Id].Equals(item);
}
public void CopyTo(AdvancedEnum[] array, int arrayIndex)
{
allValuesById.Values.CopyTo(array, arrayIndex);
}
public IEnumerator<AdvancedEnum> GetEnumerator()
{
return allValuesById.Values.GetEnumerator();
}
public bool Remove(AdvancedEnum item)
{
//values don't change after population; there's no good reason to allow this method
throw new InvalidOperationException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)allValuesById.Values).GetEnumerator();
}
}
public class DuplicateException<T> : ArgumentException
{
//for simplicity we'll hardcode the messages here; in reality these should be populated from language specific resource files.
const string KeyIsNotUnique = "Key value '{0}' is not unique within collection.";
const string PropertyValueIsNotUnique = "Value '{0}' is not unique within collection for property '{1}'.";
public DuplicateException() : base() { }
public DuplicateException(string message) : base(message) { }
public DuplicateException(string message, Exception innerException) : base(message, innerException) { }
public DuplicateException(T key) : this(string.Format(KeyIsNotUnique, key)) { }
public DuplicateException(T key, string propertyName) : this(string.Format(PropertyValueIsNotUnique, key, propertyName)) { }
public DuplicateException(T key, Exception innerException) : this(string.Format(KeyIsNotUnique, key), innerException) { }
public DuplicateException(T key, string propertyName, Exception innerException) : this(string.Format(PropertyValueIsNotUnique, key, propertyName), innerException) { }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment