See the code below. I want a class that automatically enumerates all the defined static readonly instances of its own type (see TestClass as an example, it defines 3 static readonly instances of its own type).
I want this automation because I want to loop over the defined types and not risk the change of forgetting to add a new instance to the list of All
.
Ok, I have it working, that is not the point. But why doesn't FillAll
work when called from a static constructor? See the commented static constructor in DefinedInstancesBase<T>
code. I mean FieldInfo.GetValue(null)
returns null in the static constructor, though the debugger has already hit creating the static readonly instances before the FieldInfo.GetValue(null)
is called.
I'm very curious why it doesn't work. Is this by design?
public abstract class DefinedInstancesBase<T>
{
public static IList<T> All
{
get
{
if (_All == null)
{
FillAll();
}
return _All;
}
}
//Why this doesn't work? No idea.
//static DefinedInstancesBase()
//{
// FillAll();
//}
private static void FillAll()
{
var typeOfT = typeof(T);
var fields = typeOfT.GetFields(BindingFlags.Public | BindingFlags.Static);
var fieldsOfTypeT = fields.Where(f => f.FieldType == typeOfT);
_All = new List<T>();
foreach (var fieldOfTypeT in fieldsOfTypeT)
{
_All.Add((T)fieldOfTypeT.GetValue(null));
}
}
private static List<T> _All = null;
}
[TestClass]
public class DefinedInstancesTest
{
[TestMethod]
public void StaticReadOnlyInstancesAreEnumerated()
{
//Given
var expectedClasses = new List<TestClass>
{
TestClass.First,
TestClass.Second,
TestClass.Third,
};
//When
var actualClasses = TestClass.All;
//Then
for (var i=0; i<expectedClasses.Count; i++)
{
Assert.AreEqual(expectedClasses[i].Id, actualClasses[i].Id);
}
}
private class TestClass : DefinedInstancesBase<TestClass>
{
public static readonly TestClass First = new TestClass(1);
public static readonly TestClass Second = new TestClass(2);
public static readonly TestClass Third = new TestClass(3);
public int Id { get; private set; }
private TestClass(int pId)
{
Id = pId;
}
}
}
There are two separate issues at work here.
static
constructor in the code above. Try changingstatic DefinedInstances()
tostatic DefinedInstancesBase()
, because currently it is just specified as a private static function.First
field in the derived class. Therefore,First
is still null when thestatic
constructor ofDefinedInstancesBase
class is being called (and thus theFindAll()
method).See the following code (slightly modified to better illustrate the issue) and output: