Если нет, то... фиктивный член это!
Как называется лучший статический конструктор базового класса?
Ответ 1
Правила здесь очень сложны, а между CLR 2.0 и CLR 4.0 они фактически изменились тонким и интересным образом, что IMO делает самые "умные" подходы хрупкими между версиями CLR. Метод Initialize()
также может не выполнять задание в CLR 4.0, если он не касается полей.
Я бы искал альтернативный дизайн или, возможно, использовал регулярную ленивую инициализацию в вашем типе (например, проверьте бит или ссылку (против null
), чтобы убедиться, что это было сделано).
Ответ 2
Вы можете вызвать статическую конструкторскую экспликацию, поэтому вам не нужно создавать какие-либо методы для инициализации:
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);
Вы можете вызвать его в статическом конструкторе производного класса.
Ответ 3
Как отмечали другие, ваш анализ верен. Спектр реализован буквально здесь; поскольку ни один из членов базового класса не был вызван и экземпляр не был создан, статический конструктор базового класса не вызывается. Я вижу, как это может быть удивительно, но это строгая и правильная реализация спецификации.
У меня нет никаких советов для вас, кроме "если это больно, когда вы это делаете, не делайте этого". Я просто хотел указать, что противоположный случай может вас укусить:
class Program
{
static void Main(string[] args)
{
D.M();
}
}
class B
{
static B() { Console.WriteLine("B"); }
public static void M() {}
}
class D: B
{
static D() { Console.WriteLine("D"); }
}
Это печатает "B", несмотря на то, что был вызван "член D". M является членом D исключительно по наследству; CLR не имеет возможности отличить, было ли вызвано B.M "через D" или "через B".
Ответ 4
Я почти всегда сожалею о чем-то подобном. Статические методы и классы могут ограничить вас позже. Если вы захотите ввести некоторое специальное поведение для своего класса типа позже, вы будете в коробке.
Итак, вот небольшая вариация вашего подхода. Это немного больше кода, но он позволит вам иметь настраиваемый тип, определенный позже, который позволяет выполнять пользовательские вещи.
abstract class TypeBase
{
private static bool _initialized;
protected static void Initialize()
{
if (!_initialized)
{
Type<int>.Instance = new Type<int> {Name = "int"};
Type<long>.Instance = new Type<long> {Name = "long"};
Type<double>.Instance = new Type<double> {Name = "double"};
_initialized = true;
}
}
}
class Type<T> : TypeBase
{
private static Type<T> _instance;
public static Type<T> Instance
{
get
{
Initialize();
return _instance;
}
internal set { _instance = value; }
}
public string Name { get; internal set; }
}
Затем, когда вы приступите к добавлению виртуального метода к типу и хотите специальную реализацию для Type, вы можете реализовать это:
class TypeInt : Type<int>
{
public override string Foo()
{
return "Int Fooooo";
}
}
И затем подключите его, изменив
protected static void Initialize()
{
if (!_initialized)
{
Type<int>.Instance = new TypeInt {Name = "int"};
Type<long>.Instance = new Type<long> {Name = "long"};
Type<double>.Instance = new Type<double> {Name = "double"};
_initialized = true;
}
}
Мой совет: избегать статических конструкторов - это легко сделать. Также избегайте статических классов и, где возможно, статических элементов. Я не говорю никогда, просто экономно. Предпочитайте одноэлемент класса статическому.
Ответ 5
Во всем моем тестировании я смог получить вызов фиктивного элемента на базе, чтобы заставить базу вызвать его статический конструктор, как показано на рисунке:
class Base
{
static Base()
{
Console.WriteLine("Base static constructor called.");
}
internal static void Initialize() { }
}
class Derived : Base
{
static Derived()
{
Initialize(); //Removing this will cause the Base static constructor not to be executed.
Console.WriteLine("Derived static constructor called.");
}
public static void DoStaticStuff()
{
Console.WriteLine("Doing static stuff.");
}
}
class Program
{
static void Main(string[] args)
{
Derived.DoStaticStuff();
}
}
Другой вариант включал статический член только для чтения в производном типе, который сделал следующее:
private static readonly Base myBase = new Base();
Это, однако, похоже на хак (хотя это и делает фиктивный элемент), чтобы вызвать вызывающий базовый статический конструктор.
Ответ 6
Просто идея, вы можете сделать что-то вроде этого:
abstract class TypeBase
{
static TypeBase()
{
Type<int>.Name = "int";
Type<long>.Name = "long";
Type<double>.Name = "double";
}
}
class Type<T> : TypeBase
{
static Type()
{
new Type<object>();
}
public static string Name { get; internal set; }
}
class Program
{
Console.WriteLine(Type<int>.Name);
}
Ответ 7
Вы можете попробовать следующее:
public class Program
{
public static void Main(string[] args)
{
Instance.UpdateIndexForItem(new Item { Id = 1 });
}
}
public class Instance : BaseAbstract<Instance, Item>
{
static Instance()
{
Console.WriteLine(nameof(Instance));
UpdateItem = new Action<Item>(UpdateIndexForItem);
}
public static void UpdateIndexForItem(Item item)
{
Console.WriteLine("Instance UpdateIndexForItem Method Call!");
}
}
public abstract class BaseAbstract<TInstance, TItem> where TItem : Item
{
protected static Action<TItem> UpdateItem { get; set; }
static BaseAbstract()
{
Activator.CreateInstance(typeof(TInstance));
}
public static void AddItemsToIndex(IEnumerable<TItem> items)
{
foreach (var item in items)
{
UpdateItem(item);
}
}
}
public class Item
{
public long Id { get; set; }
}