Инициализация статических полей в C # для использования в шаблоне enum - PullRequest
3 голосов
/ 27 сентября 2011

Мой вопрос на самом деле о том, как обойти, как C # инициализирует статические поля.Я должен сделать это, пытаясь продублировать перечисление в стиле Java.Ниже приведен пример кода, который показывает проблему:

Базовый класс для всех моих перечислений, наследуемых от

public class EnumBase
{
    private int _val;
    private string _description;

    protected static Dictionary<int, EnumBase> ValueMap = new Dictionary<int, EnumBase>();

    public EnumBase(int v, string desc)
    {
        _description = desc;
        _val = v;
        ValueMap.Add(_val, this);
    }

    public static EnumBase ValueOf(int i)
    {
        return ValueMap[i];
    }

    public static IEnumerable<EnumBase> Values { get { return ValueMap.Values; } }

    public override string ToString()
    {
        return string.Format("MyEnum({0})", _val);
    }
}

Образец перечисляемого набора:

public sealed class Colors : EnumBase
{
    public static readonly Colors Red    = new Colors(0, "Red");
    public static readonly Colors Green  = new Colors(1, "Green");
    public static readonly Colors Blue   = new Colors(2, "Blue");
    public static readonly Colors Yellow = new Colors(3, "Yellow");

    public Colors(int v, string d) : base(v,d) {}
}

Вот в чем проблема:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("color value of 1 is " + Colors.ValueOf(2)); //fails here
    }
}

Приведенный выше код завершается ошибкой, потому что EnumBase.ValueMap содержит ноль элементов, поскольку ни один из конструкторов для Color еще не был вызван.Кажется, что это не должно быть так сложно сделать, это возможно в Java, я чувствую, что, должно быть, здесь что-то упущено?

Ответы [ 4 ]

6 голосов
/ 27 сентября 2011

Эта модель в принципе не будет работать.Иметь один словарь тоже не будет хорошей идеей - я подозреваю, что вы хотите сделать свой EnumBase абстрактный и общий:

public abstract class EnumBase<T> where T : EnumBase<T>

, который затем может иметь защищенный * 1006.* статический член, который может быть эффективно «опубликован» через каждый производный класс:

public abstract class EnumBase<T> where T : EnumBase<T>
{
    protected static T ValueOfImpl(int value)
    {
        ...
    }
}

public class Color : EnumBase<Color>
{
    // static fields

    // Force initialization on any access, not just on field access
    static Color() {}

    // Each derived class would have this.
    public static Color ValueOf(int value)
    {
        return ValueOfImpl(value);
    }
}

Это затем вынуждает вас обращаться к самому классу Color ... в этот момент поля будут инициализированы из-застатический инициализатор.

К сожалению, нужно сделать несколько вещей, чтобы все это заработало: (

2 голосов
/ 27 сентября 2011

Я считаю, что то, что вы пытались сказать, в коде просто:

public enum Colors
    {
        [Description("Red")]
        Red = 0,
        [Description("Green")]
        Green = 1,
        [Description("Blue")]
        Blue = 2
        //etc...
    }

Вы можете легко прочитать атрибут Description, используя отражение. Вы можете даже создать методы расширения для перечислений Colors, если хотитеи реализовать что-то похожее на ValueOf в качестве метода расширения

1 голос
/ 27 сентября 2011

Вы упускаете точку статического члена. Статический член является членом типа , а не экземпляром.

Когда вы вызываете Colors.ValueOf(), вы только получаете доступ к типу, экземпляр этого типа не был создан - конструкторы экземпляра вообще не будут вызываться.

Вы можете создать метод расширения для перечисления Color, если хотите просто определить некоторые варианты поведения. Вы можете получить enum из значения, просто приведя его int base:

public enum Color { Red = 0, Green = 1, Blue = 2, Yellow = 3 }

public static class ColorExtensions
{
    public static string GetString(this Color color)
    {
        return string.Format("MyEnum({0})", color);
    }
}

Console.WriteLine("color value of 1 is " + ((Color)1).GetString());

Вы также найдете ряд полезных методов в классе System.Enum (http://msdn.microsoft.com/en-us/library/system.enum.aspx).. Есть методы для анализа из string или для получения набора всех возможных значений enum.

0 голосов
/ 03 мая 2017

Это можно сделать, и это на самом деле весьма полезно.Вы получаете безопасность типов при установке переменных и возможность динамического поиска.Я бы предпочел видеть меньше кода в дочернем классе, но, тем не менее, он работает хорошо.Вы также можете расширить это и увеличить количество полей в EnumBase, а также переопределить операторы и методы ToString и т. Д. Вы также можете добавить больше обобщений в микс.Это Enums на стероидах.

Отредактировано: чтение нижней части записи

EnumBase:

public class EnumBase
{
    public int Val { get; private set; }
    public string Description { get; private set; }

    private static readonly Dictionary<int, EnumBase> ValueMap = new Dictionary<int, EnumBase>();

    protected EnumBase(int v, string desc)
    {
        Description = desc;
        Val = v;
    }

    protected static void BuildDictionary<T>()
    {

        var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);
        foreach (var field in fields)
        {
            ValueMap.Add(((EnumBase)field.GetValue(null)).Val, (EnumBase)field.GetValue(null));
        }
    }

    public static EnumBase ValueOf(int i)
    {
        return ValueMap[i];
    }
}

Цвета:

public sealed class Colors : EnumBase
{
    public static readonly Colors Red = new Colors(0, "Red");
    public static readonly Colors Green = new Colors(1, "Green");
    public static readonly Colors Blue = new Colors(2, "Blue");
    public static readonly Colors Yellow = new Colors(3, "Yellow");

    public Colors(int v, string d) : base(v, d)
    {
    }

    static Colors()
    {
        BuildDictionary<Colors>();
    }
}

Использование:

//example of type safety
var i = Colors.Blue.Val;

//example of dynamic search
Console.WriteLine(Colors.ValueOf(1).Description);

Отредактировано:

Приведенный выше код не работает, если вы наследуете от EnumBase более одного раза (что является огромной проблемой).Статические методы не наследуются, т.е. все дочерние классы просто добавят больше записей в словарь статического базового класса.

Если сценарий использования достаточно силен, вы можете повторно использовать код вместо попытки использовать наследование:

public sealed class Colors 
{
    public int Val { get; private set; }
    public string Description { get; private set; }

    private static readonly Dictionary<int, Colors> ValueMap = new Dictionary<int, Colors>();

    static Colors()
    {
        BuildDictionary<Colors>();
    }

    private static void BuildDictionary<T>()
    {
        var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);
        foreach (var field in fields)
        {
            ValueMap.Add(((Colors)field.GetValue(null)).Val, (Colors)field.GetValue(null));
        }
    }

    public static Colors ValueOf(int i)
    {
        return ValueMap[i];
    }

    private Colors(int v, string desc)
    {
        Description = desc;
        Val = v;
    }

    public static readonly Colors Red = new Colors(0, "Red");
    public static readonly Colors Green = new Colors(1, "Green");
    public static readonly Colors Blue = new Colors(2, "Blue");
    public static readonly Colors Yellow = new Colors(3, "Yellow");

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...