Consider the following code:
class Program
{
static Program() {
Program.program1.Value = 5;
}
static List<Program> values = new List<Program>();
int value;
int Value
{
get 开发者_运维百科{ return value; }
set {
this.value = value;
Program.values.Add(this);
}
}
static Program program1 = new Program { value = 1 };
static Program program2 = new Program { value = 2 };
static Program program3 = new Program { value = 3 };
static void Main(string[] args)
{
if (Program.values.Count == 0) Console.WriteLine("Empty");
foreach (var value in Program.values)
Console.WriteLine(value.Value);
Console.ReadKey();
}
}
It prints only the number 5, and if removed the code in the static constructor, it prints "Empty".
Is there a way to force static fields to be initialized even whether not used yet?
I need to have a static property named Values with returns all instances of the referred type.
I tried some variations of this code and some works for some types but doesn't for others.
EDIT: THE SAMPLE ABOVE IS BROKEN, TRY THIS ONE:
class Subclass<T> {
static Subclass()
{
Values = new List<Subclass<T>>();
}
public Subclass()
{
if (!Values.Any(i => i.Value.Equals(this.Value)))
{
Values.Add(this);
}
}
public T Value { get; set; }
public static List<Subclass<T>> Values { get; private set; }
}
class Superclass : Subclass<int>
{
public static Superclass SuperclassA1 = new Superclass { Value = 1 };
public static Superclass SuperclassA2 = new Superclass { Value = 2 };
public static Superclass SuperclassA3 = new Superclass { Value = 3 };
public static Superclass SuperclassA4 = new Superclass { Value = 4 };
}
class Program
{
static void Main(string[] args)
{
//Console.WriteLine(Superclass.SuperclassA1); //UNCOMMENT THIS LINE AND IT WORKS
foreach (var value in Superclass.Values)
{
Console.WriteLine(value.Value);
}
Console.ReadKey();
}
}
There is actually a way to force the initialization of the properties in this case. The change requires adding a type parameter to the base class to represent the future subclass of the base class that will contain the fields to be initialized. Then we can use the RuntimeHelpers.RunClassConstructor to ensure that the sub class static fields are initialized.
The following will yield the results that you are looking for:
class Subclass<TSubclass, T>
{
static Subclass()
{
Values = new List<Subclass<TSubclass, T>>();
// This line is where the magic happens
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle);
}
public Subclass()
{
if (!Values.Any(i => i.Value.Equals(this.Value)))
{
Values.Add(this);
}
}
public T Value { get; set; }
public static List<Subclass<TSubclass, T>> Values { get; private set; }
}
class Superclass : Subclass<Superclass, int>
{
public static Superclass SuperclassA1 = new Superclass { Value = 1 };
public static Superclass SuperclassA2 = new Superclass { Value = 2 };
public static Superclass SuperclassA3 = new Superclass { Value = 3 };
public static Superclass SuperclassA4 = new Superclass { Value = 4 };
}
public class Program
{
public static void Main()
{
foreach (var value in Superclass.Values)
{
Console.WriteLine(value.Value);
}
Console.ReadKey();
}
}
What is happening is, the call to RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle)
forces the static constructor of TSubclass
to execute if it has not already run. This ensures that the static fields have initialized first as per this line from https://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx :
If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor.
Here is a dotnetfiddle demonstrating it working:
https://dotnetfiddle.net/MfXzFd
The answer to your question is 'well, yes'. But one of the two ways of "forcing" it is what you're already doing.
The relevant section in the language spec is Static constructors, and specifically:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.
If a class contains the Main method (Section 3.1) in which execution begins, the static constructor for that class executes before the Main method is called. If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor.
But you're never setting the property -- instead you're setting the backing field directly, so not going through your logic to add to the static list when creating program1, program2 and program3.
i.e. you need to change:
static Program program1 = new Program { value = 1 };
static Program program2 = new Program { value = 2 };
static Program program3 = new Program { value = 3 };
to:
static Program program1 = new Program { Value = 1 };
static Program program2 = new Program { Value = 2 };
static Program program3 = new Program { Value = 3 };
Actually looks you misspelled 'value' -> 'Value' So:
static Program program1 = new Program { Value = 1 };
static Program program2 = new Program { Value = 2 };
static Program program3 = new Program { Value = 3 };
pretty prints more lines
The second sample doesn't work simply because Value
is a static member of Subclass
.
C# syntax allows Superclass.Values
, but eventually the compiled method call will be to the Subclass.Values
getter. So the type Superclass
is never actually touched. Superclass.SuperclassA1
on the other hand does touch the type and triggers the static initialization.
This is why C# doesn't really have implicit static initialization and you need composition frameworks like MEF and Unity.
精彩评论