C#泛型类型中静态字段的初始化
我从C#静态字段初始值设定项“在第一次使用该类的静态字段之前执行”,但这仍然会产生我没有预料到的结果,至少对于泛型类型 来自Java世界的我错过了丰富的枚举,我认为使用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
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) {
}
}