2016-01-11 2 views
4

В настоящее время мы реализуем какой-то «расширяемый класс enum» на основе строк. Ниже приведена только часть этого кода C#, чтобы облегчить понимание проблемы.Инициализация статических полей C#

Если я запустил код ниже, он записывает «BaseValue1» и «BaseValue2» на консоль.

Если я раскомментирую строку RunClassConstructor и запустил код, он дополнительно записывает «DerivedValue1» и «DerivedValue2» на консоль.
Этого я хочу достичь, но я хочу его достичь безRunClassConstructor линия.

Я думал, что DerivedEnum.AllKeys вызовет создание «DerivedValue1» и «DerivedValue2», но, очевидно, это не так.

Есть ли возможность достичь того, чего я хочу, не заставляя пользователя этих «классов enum» писать какой-то магический код или делать какую-то фиктивную инициализацию?

using System; 
using System.Collections.Generic; 

namespace ConsoleApplication 
{ 
    public class Program 
    { 
     static void Main() 
     { 
      //System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(DerivedEnum).TypeHandle); 

      foreach (var value in DerivedEnum.AllKeys) 
      { 
       Console.WriteLine(value); 
      } 
     } 
    } 

    public class BaseEnum 
    { 
     private static readonly IDictionary<string, BaseEnum> _dictionary = new Dictionary<string, BaseEnum>(); 

     public static ICollection<string> AllKeys 
     { 
      get 
      { 
       return _dictionary.Keys; 
      } 
     } 

     public static readonly BaseEnum BaseValue1 = new BaseEnum("BaseValue1"); 
     public static readonly BaseEnum BaseValue2 = new BaseEnum("BaseValue2"); 

     protected BaseEnum(string value) 
     { 
      _dictionary[value] = this; 
     } 
    } 

    public class DerivedEnum : BaseEnum 
    { 
     public static readonly DerivedEnum DerivedValue1 = new DerivedEnum("DerivedValue1"); 
     public static readonly DerivedEnum DerivedValue2 = new DerivedEnum("DerivedValue2"); 

     protected DerivedEnum(string value) 
      : base(value) 
     { 
     } 
    } 
} 
+1

[Когда статические переменные инициализируются в C#?] (Http: // stackoverflow.com/questions/3965976/when-do-static-variables-get-initialized-in-c), [Есть ли способ принудительно инициализировать статические поля в C#?] (http://stackoverflow.com/questions/ 2154697/is-there-a-way-to-force-static-fields-to-be-initial-in-c), поэтому вам нужно _instantiate_ 'DerivedEnum' или получить доступ к одному из статических членов _its_. – CodeCaster

ответ

2

Статический конструктор запускается только при первом доступе к классу.

Пока вы использовали DerivedEnum.AllKeys, но он просто унаследован от BaseEnum. Поэтому DerivedEnum никогда не ссылались напрямую.

Небольшой взлом, который вы могли бы сделать, на самом деле создает свойство new static на DerivedEnum, которое возвращает то же свойство из базового класса, поэтому, когда он называется статическим конструктором производного класса, будет вызываться.

public class DerivedEnum : BaseEnum 
{ 
    public new static ICollection<string> AllKeys 
    { 
     get 
     { 
      return BaseEnum.AllKeys; 
     } 
    } 
} 

UPDATE

Вы можете также использовать System.Reflexion динамически вызывать их:

public class BaseEnum 
    static BaseEnum() 
    { 
     // from the types defined in current assembly 
     Assembly.GetExecutingAssembly().DefinedTypes 
      // for those who are BaseEnum or its derived 
      .Where(x => typeof(BaseEnum).IsAssignableFrom(x)) 
      // invoke their static ctor 
      .ToList().ForEach(x => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.TypeHandle)); 
    } 
} 

Вы также можете использовать этот код для инициализации производного класса, определенного в других сборках:

AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(x => x.DefinedTypes) 
    .Where(x => typeof(BaseEnum).IsAssignableFrom(x)) 
    .ToList().ForEach(x => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.TypeHandle)); 
+0

Спасибо. Существует ли также решение, в котором необходима только модификация в «BaseEnum», так что разработчику «DerivedEnum» не нужно заботиться о внедрении нового статического свойства? Вероятно, нет, потому что 'BaseEnum' не знает всех своих производных классов ... – MaDev

+0

Вы также можете использовать' System.Reflexion' для динамического вызова их. Смотрите мое обновление. – Xiaoy312

+0

Хорошо, это было бы хорошим решением, если бы все производные классы были в той же Dll, что и класс BaseEnum. В моем коде это, к сожалению, не так, потому что пользователи нашей библиотеки могут добавлять собственные производные классы в дополнительные Dll. – MaDev

1

I n C#, статические члены инициализируются непосредственно перед первым использованием класса. В вашем примере вы фактически используете элемент базового класса BaseEnum и обходите DerivedEnum, который вызывает инициализацию только статических элементов BaseEnum.

Вам необходимо будет реализовать свойство AllKeys в производном классе. Это гарантирует, что компилятор использует свойство в производном классе &, инициализирует все его члены.

А затем добавить новый AllKeys объект в DerivedEnum, чтобы переопределить AllKeys of BaseEnum.

new public static ICollection<string> AllKeys 
{ 
    get 
    {  
     return BaseEnum.AllKeys; 
    } 
} 
+0

Спасибо за ваш ответ. Наверное, вы имеете в виду 'AllKeys' _property_, а не' AllKeys'_field_, правильно? – MaDev

+0

@MaDev Правильно, спасибо, что указали это. Я обновлю ответ. –

Смежные вопросы