Я понял из этого ответа , что инициализаторы статического поля C # "выполняются ... до первого использования статического поля этого класса", но это все равно дает результаты, которых я не ожидал, по крайней мере, с общими типами.
Исходя из мира Java, мне не хватало моих богатых перечислений, и я подумал с более серьезными обобщениями C #, что я должен быть в состоянии воспроизвести их с минимальным количеством шаблонов. Вот (без каких-либо деталей, таких как сопоставимость) вот что я придумал:
public class AbstractEnum<T> where T : AbstractEnum<T>
{
static readonly IDictionary<String, T> nameRegistry = new Dictionary<String, T>();
readonly String name;
protected AbstractEnum (String name)
{
this.name = name;
nameRegistry[name] = (T) this;
}
public String Name {
get {
return name;
}
}
public static T ValueOf(String name) {
return nameRegistry[name];
}
public static IEnumerable<T> Values {
get {
return nameRegistry.Values;
}
}
}
И некоторые примеры подклассов:
public class SomeEnum : AbstractEnum<SomeEnum> {
public static readonly SomeEnum V1 = new SomeEnum("V1");
public static readonly SomeEnum V2 = new SomeEnum("V2");
SomeEnum(String name) : base(name) {
}
}
public class OtherEnum : AbstractEnum<OtherEnum> {
public static readonly OtherEnum V1 = new OtherEnum("V1");
public static readonly OtherEnum V2 = new OtherEnum("V2");
OtherEnum(String name) : base(name) {
}
}
Это выглядит хорошо и более или менее делает свое дело ... за исключением того, что, следуя букве спецификации, фактические экземпляры (SomeEnum.V1
, OtherEnum.V1
и т. Д.) Не инициализируются, если хотя бы один из на них ссылаются в явном виде. Статические поля / методы в базовом классе не учитываются. Так, например, следующее:
Console.WriteLine("Count: {0}", SomeEnum.Values.Count());
foreach (SomeEnum e in SomeEnum.Values) {
Console.WriteLine(e.Name);
}
пишет Count: 0
, но если я добавлю следующую строку -
Console.WriteLine("SomeEnum.V1: " + SomeEnum.V1.Name);
- даже после выше, я получаю:
Count: 2
V1
V2
(Кстати, обратите внимание, что инициализация экземпляров в статическом конструкторе не имеет значения.)
Теперь я могу это исправить, пометив nameRegistry
как protected
и вставив Values
и ValueOf
в подклассы, но я надеялся сохранить всю сложность в суперклассе и довести шаблон до минимум. Может ли кто-нибудь, чей C # -fu превосходит мой, придумать хитрость для того, чтобы сделать экземпляры подкласса «самореализующимися»?
Примечание: FWIW, это в Mono, на Mac OS. YM в MS .NET, в Windows, MV.
ETA: Для разработчиков моноглота C # (или даже разработчиков полиглотов, чей опыт ограничен языками, начинающимися с 'C'), задающихся вопросом WTF, я пытаюсь сделать: this . Перечисления C # заботятся о безопасности типов, но им все еще не хватает всего.