Note: The following code actually works okay, but shows the scenario that is failing in my own solution. See the bottom of this post for more information.
With these classes:
public class MainType {
public static readonly MainType One = new MainType();
public static readonly MainType Two = SubType.Two;
}
public sealed class SubType : MainType {
public new static readonly SubType Two = new SubType();
}
Get fields One
and Two
:
List<FieldInfo> fieldInfos = typeof(MainType)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(f => typeof(MainType).IsAssignableFrom(f.FieldType))
.ToList();
Finally, get their values:
List<MainType> publicMainTypes = fieldInfos
.Select(f => (MainType) f.GetValue(null))
.ToList();
In LinqPad or in a simple unit test class with the above code, everything works okay. But in my solution, where I have some unit tests that want to work with all instances of these fields, GetValue
works fine to return fields of the parent type, but where the parent fields are supposed have instances of the subtype, they always instead give null
! (If that happened here, the final list would be { One, null }
instead of { One, Two }
.) The test class is in a different project from the two types (each in their own file), but I've temporarily made everything public. I've dropped a breakpoint in and have examined all I can examine, and have done the equivalent of fieldInfos[1].GetValue(null)
in a Watch expression and it does in fact return null, despite the fact that there is a line in my main class exactly like the second one from MainType
above.
What is wrong? How do I get all the values of the subtype fields? How is it even possible for them to return null without an error?
On the theory that perhaps for some reason the subtype's class was not being statically constructed due to the access through reflection, I tried
System.Runtime.CompilerServices.RuntimeHelpers
.RunClassConstructor(typeof(SubType).TypeHandle);
at the top before starting, but it didn't help (where SubType
is the actual subtype class in my project).
I'll keep plugging away at trying to reproduce this in a simple case, but I'm out of ideas for the moment.
Additional Information
After a bunch of fiddling, the code started working. Now it is not working again. I am working on reproducing what triggered the code to start working.
Note: Targeting .Net 4.6.1 using C# 6.0 in Visual Studio 2015.
Problem Reproduction Available
You can play with a working (failing) trimmed-down version of my scenario by downloading this somewhat minimal working example of the problem at github.
Debug the unit tests. When the exception occurs, step until you get to line 20 of GlossaryHelper.cs, and can see the return value of GetGlossaryMembers
in the Locals
tab. You can see that indexes 3 through 12 are null.
Problem
The issue has nothing to do with Reflection but instead the circular dependency between the two static field initializers and the order of their execution.
Consider the following snippet:
var b = MainType.Two;
var a = SubType.Two;
Debug.Assert(a == b); // Success
Now let's exchange the first two lines:
var a = SubType.Two;
var b = MainType.Two;
Debug.Assert(a == b); // Fail! b == null
So what's going on here? Let's see:
SubType.Two
static field for the first time. SubType
. SubType
inherits from MainType
, the MainType
constructor also executes and triggers MainType
static initialization. MainType.Two
field static initializer is trying to access SubType.Two
. Since static initializers are executed just once, and the one for SubType.Two
is already executed (well, not really, it's currently executing, but is considered to be), it simply returns the current field value (null
at that moment) which then is stored in MainType.Two
and will be returned by further access requests for the field.In short, the correct working of such design really depends on the order of the external access to the fields, so it's not surprising that it sometimes works, sometimes not. Unfortunately this is something you cannot control.
How to fix
If possible, avoid such static field dependencies. Use static readonly properties instead. They give you full control and also allows you to eliminate the field duplication (currently you have 2 different fields that hold one and the same value).
Here is the equivalent design without such issues (using C#6.0):
public class MainType
{
public static MainType One { get; } = new MainType();
public static MainType Two => SubType.Two;
}
public sealed class SubType : MainType
{
public new static SubType Two { get; } = new SubType();
}
Of course this will require changing your reflection code to use GetProperties
instead of GetFields
, but I think it's worth it.
Update: Another way to resolve the issue is to move the static fields to a nested abstract container classes:
public class MainType
{
public abstract class Fields
{
public static readonly MainType One = new MainType();
public static readonly MainType Two = SubType.Fields.Two;
}
}
public sealed class SubType : MainType
{
public new abstract class Fields : MainType.Fields
{
public new static readonly SubType Two = new SubType();
}
}
Now both tests complete successfully:
var a = SubType.Fields.Two;
var b = MainType.Fields.Two;
Debug.Assert(a == b); // Success
and
var b = MainType.Fields.Two;
var a = SubType.Fields.Two;
Debug.Assert(a == b); // Success
This is because the container classes, except of being contained in, have no relation to the static field types, hence their static initialization is independent. Also, although they use inheritance, they never get instantiated (because of being abstract
), thus have no side effects caused by the base constructor calls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With