How to trigger a static constructor
code:
class Base<T,U> where T:Base<T,U>,new() where U :class
{
protected static U _val = null;
internal static void ShowValue()
{
if(_val == null)new T(); //Without this line, it won't work as expected
Console.WriteLine (_val);
}
internal static void Virtual()
{
Console.WriteLine ("Base");
}
}
class Deriv :Base<Deriv,string>
{
static Deriv()
{
_val = "some string value";
}
internal static new void Virtual ()
{
Console.WriteLine ("Deriv");
}
}
public static void Main (string[] args)
{
Deriv.ShowValue();
Deriv.Virtual();
}
Thanks to the generics of .NET, I can create a bunch of specific classes reusing generic static methods defined in the generic base class. It can mimic inheritance polymorphism to some extent. But in order to initialize different version of static fields, I've to use static constructors. Unfortunately, we can't call them directly, therefore, we have to figure out a way to trigger it's invocation. The example given above showed a way. But I don't like either the instantiation,or the reflection approach. We also can't make a constraint on a static method of a generic parameter. So, I'd like to ask, if there is another way to do this kind of job!
Thanks beforehand!
~~~~~~~~~~~~~~~~
Some Conclusion (Maybe a little early):
It seems there is no workaround to deal with this kind of situation. I have to instantiate a subclass or use reflection. Considering the .cctors need merely be called once, I'm in favor of the reflection approach, because in some case, a new() constraint is just not a choice - like you're not supposed to expose the parameterless ctor to user.
After conducting further experiment, I find out that the .cctors may be called multi-times but only the first invocation will affect the setting of static fields. That's weird, but a good weirdness!
class MyClass
{
static int _val = 0;
static My开发者_C百科Class()
{
_val++;
Console.WriteLine (_val);
}
}
public static void Main (string[] args)
{
ConstructorInfo ci = typeof(MyClass).TypeInitializer;
ci.Invoke(new object[0]);
ci.Invoke(new object[0]);
ci.Invoke(new object[0]);
}
//result:
//1
//1
//1
//1
I would strongly advise you to rethink your design. Attempting to use this sort of workaround for "static inheritance" is fighting against some of the core designs of .NET.
It's unclear what bigger problem you're trying to solve, but trying "clever" code like this to simulate inheritance will lead to code which is very hard to maintain and diagnose in the longer term.
Without actually using a member of Deriv
(or creating an instance of it), you basically won't trigger the static constructor. It's important to understand that Deriv.ShowValue()
is basically converted into a call to
Base<Deriv, string>.ShowValue();
... so you're not actually calling anything on Deriv
. Your calling code would actually be clearer if it were written that way.
EDIT: One other (clearly unfortunate) reason to avoid using type initializers explicitly is that there's a bug in .NET 4.5 which causes an exception to be thrown inappropriately in some cases. See my question on the topic for more information.
The correct solution is to invoke the type initializer (= static constructor) like this:
typeof(T).TypeInitializer.Invoke(null, null);
It needs both null
s. Specifying only one gives a MemberAccessException
.
Thus, your code might want to look something like this:
internal static void ShowValue()
{
if (_val == null)
{
if (typeof(T).TypeInitializer != null)
typeof(T).TypeInitializer.Invoke(null, null);
if (_val == null)
throw new InvalidOperationException(string.Format("The type initializer of {0} did not initialize the _val field.", typeof(T)));
}
Console.WriteLine(_val);
}
And with that, you can remove the new()
constraint.
You have no control of when the static constuctor will execute, but what is guaranteed is that it will run before accessing any static property or method and before instantiation.
There is really no reason to want the static constructor to execute at an earlier point. If you are not using anything from the class but you want the code in the static constructor to run, then something is wrong in your design.
Static constructors are automatically, only once. You cannot call them yourself.
An example from here:
public class Bus
{
// Static constructor:
static Bus()
{
System.Console.WriteLine("The static constructor invoked.");
}
public static void Drive()
{
System.Console.WriteLine("The Drive method invoked.");
}
}
class TestBus
{
static void Main()
{
Bus.Drive();
}
}
output:
The static constructor invoked.
The Drive method invoked.
I direct you to the MSDN article on Static Constructors and about 10% down the page:
A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.
精彩评论