Хотели бы вы с этим сильно печатать?
Хотя ваше решение было продуманным, я думаю, что это может немного лучше удовлетворить ваши потребности, особенно в отношении повторного использования кода.
WellKnownStrings
public class WellKnownStrings : StringEnumeration
{
private WellKnownStrings(string specialString) :base(specialString)
{
}
public static IEnumerable<String> SpecialStrings
{
get
{
return GetAllStrings<WellKnownStrings>();
}
}
public static readonly WellKnownStrings String1 = new WellKnownStrings("SOME_STRING_1");
public static readonly WellKnownStrings String2 = new WellKnownStrings("SOME_STRING_2_SPECIAL");
public static readonly WellKnownStrings String3 = new WellKnownStrings("SOME_STRING_3_SPECIAL");
}
StringEnumeration
Это базовый класс, который я адаптировал для выполнения именно того, что вы описываете.
public abstract class StringEnumeration : Enumeration
{
private static int _nextItemValue;
private static readonly object _initializeLock = new object();
protected StringEnumeration(string stringValue)
:base(0, stringValue)
{
if(stringValue == null)
{
throw new ArgumentNullException("stringValue");
}
lock(_initializeLock)
{
_nextItemValue++;
_value = _nextItemValue;
}
}
public static IEnumerable<string> GetAllStrings<T>()
where T: StringEnumeration
{
return GetAll<T>().Select(x => x.DisplayName);
}
private readonly int _value;
public override int Value
{
get
{
return _value;
}
}
public static explicit operator string(WellKnownStrings specialStrings)
{
return specialStrings.ToString();
}
}
Базовый класс перечисления
Код, первоначально украденный и адаптированный из блога Джимми Богарда Единственные изменения, которые я сделал, - сделать свойство Value
виртуальным в производном классе и сделать GetAll () не зависит от универсального параметра new T()
, поскольку статическим полям-членам не требуется экземпляр для рефлексивного получения значения.
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public virtual int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
return typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
.Where(field => field.FieldType == typeof (T))
.Select(field => field.GetValue(null))
.Where(value =>value != null)
.Cast<T>();
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new Exception(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(field=>);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new Exception(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
Кроме того, о потоке безопасной проблемы ...
Класс I предоставил многопоточный, интуитивно понятный и многократно используемый. Ваш вариант использования ReadOnlyCollection<T>
также был поточно-ориентированным, но, как указал herzmeister der welten, во многих сценариях это не так. Он также фактически не предоставляет доступным для записи членам ICollection, потому что любые вызовы этих исключений выдают.