C#泛型类型中静态字段的初始化

C#泛型类型中静态字段的初始化,c#,generics,initialization,C#,Generics,Initialization,我从C#静态字段初始值设定项“在第一次使用该类的静态字段之前执行”,但这仍然会产生我没有预料到的结果,至少对于泛型类型 来自Java世界的我错过了丰富的枚举,我认为使用C#的更严肃的泛型,我应该能够用最少的样板文件复制它们。这里(去掉一些细节,比如可比性)是我想到的: public class AbstractEnum<T> where T : AbstractEnum<T> { static readonly IDictionary<String, T&g

我从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;
        }
    }
}
写入
计数:0
,但如果我添加以下行--

--即使在完成上述步骤后,我仍然会:

Count: 2
V1
V2
(顺便说一句,请注意,在静态构造函数中初始化实例没有什么区别。)

现在,我可以通过将
nameRegistry
标记为
protected
并将
Values
ValueOf
下推到子类中来解决这个问题,但我希望保持超类中的所有复杂性,并将样板文件保持在最低限度。有谁的C#fu优于我的,能想出一个使子类实例“自动执行”的窍门吗


注意:FWIW,这是在MacOS上的Mono中。YM在MS.NET中,在Windows上,MV



ETA:对于monoglot C#开发人员(甚至是经验仅限于以“C”开头的语言的多语言开发人员),我想知道我正在尝试的WTF:。C#enum解决了类型安全问题,但它们仍然忽略了其他所有内容。

诚然,我不知道Richenum是什么,但这个C#不是你想要的吗

public enum SomeEnum
{
    V1,
    V2
}

class Program
{
    static void Main(string[] args)
    {
        var values = Enum.GetValues(typeof (SomeEnum));
        Console.WriteLine("Count: {0}", values.Length);
        foreach (SomeEnum e in values)
        {
            Console.WriteLine(e);
        }
    }
}

我想到了这个——虽然不完全令人满意,但确实做到了:

        public static IEnumerable<T> Values
        {
            get
            {
                if (nameRegistry.Count > 0)
                {
                    return nameRegistry.Values;
                }
                var aField = typeof (T).GetFields(
                                        BindingFlags.Public | BindingFlags.Static)
                                    .FirstOrDefault();

                if (aField != null)
                    aField.GetValue(null);

                return nameRegistry.Values;
            }
        }
公共静态IEnumerable值
{
得到
{
如果(nameRegistry.Count>0)
{
返回nameRegistry.Values;
}
var aField=typeof(T).GetFields(
BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault();
如果(远场!=null)
aField.GetValue(空);
返回nameRegistry.Values;
}
}
编辑这里有一个稍微不同的版本,可以解决VinayC在评论中的顾虑。问题是:线程A调用Values()。当SomeEnum的静态构造函数运行时,在添加V1之后但在添加V2之前,线程B调用值。在最初编写的代码中,它将被交给一个IEnumerable,该IEnumerable可能只产生V1。因此,如果第二个线程在第一次调用任何特定类型的Values()时调用,则可能会从Values()得到错误的结果

以下版本使用布尔标志,而不是依赖nameRegistry中的非零计数。在此版本中,反射代码仍可能运行多次,但不再可能从Values()中获得错误的答案,因为在反射代码完成时,nameRegistry保证已完全初始化

        private static bool _initialized;
        public static IEnumerable<T> Values
        {
            get
            {
                if (_initialized)
                {
                    return nameRegistry.Values;
                }
                var aField = typeof(T).GetFields(
                                            BindingFlags.Public | BindingFlags.Static)
                                        .FirstOrDefault();
                if (aField != null)
                    aField.GetValue(null);
                _initialized = true;
                return nameRegistry.Values;
            }
        }
private static bool\u初始化;
公共静态IEnumerable值
{
得到
{
如果(_已初始化)
{
返回nameRegistry.Values;
}
var aField=typeof(T).GetFields(
BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault();
如果(远场!=null)
aField.GetValue(空);
_初始化=真;
返回nameRegistry.Values;
}
}

我不喜欢下面这样的解决方案,但是

public class AbstractEnum<T> where T : AbstractEnum<T>
{
   ...
   private static IEnumerable<T> ValuesInternal {
        get {
            return nameRegistry.Values;
        }
   }

   public IEnumerable<T> Values {
     get {
       return ValuesInternal;
     }
   }
}
我会选择第二个选项。

那么:

public class BaseRichEnum 
{
   public static InitializeAll()
   {
      foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
      {
        if (t.IsClass && !t.IsAbstract && typeof (BaseRichEnum).IsAssignableFrom(t))
        {
          t.GetMethod("Initialize").Invoke(null, null); //might want to use flags on GetMethod
        }
      }
   }
}

public class AbstractEnum<T> : BaseRichEnum 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;
        }
    }    
}
公共类BaseRichEnum
{
公共静态初始化all()
{
foreach(在Assembly.getExecutionGassembly().GetTypes()中键入t)
{
if(t.IsClass&!t.isastract&&typeof(BaseRichEnum).IsAssignableFrom(t))
{
t、 GetMethod(“Initialize”).Invoke(null,null);//可能要在GetMethod上使用标志
}
}
}
}
公共类AbstractEnum:BaseRichEnum,其中T:AbstractEnum
{
静态只读IDictionary nameRegistry=new Dictionary();
只读字符串名称;
受保护的抽象枚举(字符串名称)
{
this.name=名称;
名称注册表[名称]=(T)此;
}
公共字符串名{
得到{
返回名称;
}
}
公共静态T值(字符串名称){
返回名称注册表[名称];
}
公共静态IEnumerable值{
得到{
返回nameRegistry.Values;
}
}    
}
然后:

public class SomeEnum : AbstractEnum<SomeEnum> 
{

        public static readonly SomeEnum V1;
        public static readonly SomeEnum V2;

        public static void Initialize()
        {
          V1 = new SomeEnum("V1");
          V2 = new SomeEnum("V2"); 
        }

        SomeEnum(String name) : base(name) {
        }
    }
公共类SomeEnum:AbstractEnum
{
公共静态只读SomeEnum V1;
公共静态只读SomeEnum V2;
公共静态void Initialize()
{
V1=新的SomeEnum(“V1”);
V2=新的SomeEnum(“V2”);
}
SomeEnum(字符串名称):基(名称){
}
}

然后必须在应用程序启动代码中调用BaseRichEnum.InitializeAll()。我认为最好将这个简单的要求强加给客户机,从而使机制可见,而不是期望未来的维护人员掌握静态时间初始化的微妙之处。

你不能让它可靠,放弃这个想法。当你说“放弃这个想法”时,到底是哪一部分?因为我需要具有行为的枚举类型;这并没有消失。什么行为对枚举有意义?您列出的所有内容都在Enum类上使用静态方法存在。当您第一次需要向SomeEnum添加行为时,它将停止工作。我明白了。这是一个有趣的特性。我不确定我是否知道如何很好地使用它。在SomeEnum上使用扩展方法可以获得简单的行为。当然,这并不适用于所有情况,例如没有运算符重载。哦,这是真的。你就这样结束了
public class AbstractEnum<T> where T : AbstractEnum<T>
{
   ...
   private static IEnumerable<T> ValuesInternal {
        get {
            return nameRegistry.Values;
        }
   }

   public IEnumerable<T> Values {
     get {
       return ValuesInternal;
     }
   }
}
public class AbstractEnum<T> where T : AbstractEnum<T>
{
   ...
   protected static IEnumerable<T> ValuesInternal {
        get {
            return nameRegistry.Values;
        }
   }
}

public class SomeEnum : AbstractEnum<SomeEnum> {
  ...

  public static IEnumerable<SomeEnum> Values
  {
    get
    {
        return ValuesInternal;
    }

  }
}
public class BaseRichEnum 
{
   public static InitializeAll()
   {
      foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
      {
        if (t.IsClass && !t.IsAbstract && typeof (BaseRichEnum).IsAssignableFrom(t))
        {
          t.GetMethod("Initialize").Invoke(null, null); //might want to use flags on GetMethod
        }
      }
   }
}

public class AbstractEnum<T> : BaseRichEnum 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;
        public static readonly SomeEnum V2;

        public static void Initialize()
        {
          V1 = new SomeEnum("V1");
          V2 = new SomeEnum("V2"); 
        }

        SomeEnum(String name) : base(name) {
        }
    }