【问题标题】:Initializing static fields in C# for use in enum pattern在 C# 中初始化静态字段以用于枚举模式
【发布时间】:2011-09-26 21:34:07
【问题描述】:

我的问题实际上是关于解决 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中是可能的,我觉得我一定是在这里遗漏了什么?

【问题讨论】:

    标签: c# initialization enumeration static-members


    【解决方案1】:

    这种模式基本上行不通。拥有一本字典也不是一个好主意 - 我怀疑你想让你的 EnumBase 抽象和通用:

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

    然后可以有一个 protected 静态成员,可以通过每个派生类有效地“发布”:

    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 类本身...此时字段将被初始化,因为静态初始化程序。

    不幸的是,要完成所有这些工作需要做很多事情:(

    【讨论】:

    • 我的实际实现是抽象的,通用的,颜色是密封的。但是,是的,在将我的头撞到墙上之后,我似乎陷入了“枚举”、自定义属性、一个使用反射的模板化 EnumExtension 类。那将是一些干净的代码:)
    【解决方案2】:

    我相信您在代码中所说的内容很简单:

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

    您可以使用反射轻松读取 Description 属性。如果需要,您甚至可以为 Colors 枚举创建扩展方法,并实现类似于 ValueOf 的方法作为扩展方法

    【讨论】:

    • 但是与 Java 相比,C# 枚举非常有限,所以我希望能想出类似于 Java 的东西。我不确定如何将 ValueOf(int x) 实现为扩展方法。虽然无论如何都不需要它,因为 int 可以是枚举的情况。但是,这仍然是扩展的一个限制。你不能有类方法。即 MyEnum.DoSomthing(5)。您可以执行此 MyEnum 实例; instance.DoSomething(5),但这是不同的。无论哪种方式,我都意识到我必须使用普通的旧枚举,以及一​​个模板化的扩展类。
    • 我不确定你想用 MyEnum.DoSomething 完成什么。枚举(在 C++ 和 C# 中)只是一个结构化的数字列表,而不是一组数据和操作(如一个类)。
    【解决方案3】:

    您错过了静态成员的要点。静态成员是 type 的成员,而不是实例。

    当您调用 Colors.ValueOf() 时,您只是在访问该类型,尚未创建该类型的实例 - 根本不会调用实例构造函数。

    如果您只想定义一些行为,您可以为Color 枚举创建一个扩展方法。您可以通过从其int 基数转换来从一个值中获取枚举:

    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 值的集合。

    【讨论】:

    • 但是不应该调用 Colors 的静态构造函数,它将初始化所有这些静态成员变量(反过来,颜色)?
    • 是的,抱歉,我现在更符合条件了。只有实例构造函数不会被调用。
    • 我还是不明白为什么实例构造函数不会被调用。它们在被调用的静态构造函数中实例化,对吗?我在这里没有得到什么?
    • 不,实例构造函数在创建类型的实例时运行。静态构造函数在类型加载之后和访问任何类型成员之前运行。
    • 对。静态构造函数包括静态成员的初始化。因此,当运行 Colors.ValueOf(2) 时,静态 Colors 构造函数将已经运行,其中包括静态成员的初始化,例如“public static readonly Colors Red = new Colors(0, "Red");"通过初始化该静态成员,运行 Colors 的实例构造函数。 ValueOf 是否真的在基类上,通过 Colors 调用它实际上并不能保证调用 Colors 上的静态构造函数?
    【解决方案4】:

    这个是可以做到的,其实还是蛮有用的。在设置变量和动态搜索的能力时,您可以获得类型安全。我希望在子类中看到更少的代码,但无论如何,它运行良好。您还可以对此进行扩展并增加 EnumBase 中的字段数量,并覆盖运算符和 ToString 方法等。您还可以将更多泛型加入其中。这是类固醇的枚举。

    已编辑:阅读条目底部

    枚举基础:

    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 继承,上面的代码将不起作用(这是一个大问题)。静态方法不会被继承,即所有子类只会向静态基类 Dictionary 添加更多记录。

    如果用例足够强大,您可以重用代码而不是尝试使用继承:

    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");
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2011-05-15
      • 2013-10-29
      • 2011-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多