为什么 FieldInfo.GetValue(null) 在静态构造函数中不起作用
Why is FieldInfo.GetValue(null) not working in static constructor
查看下面的代码。我想要一个 class 自动枚举所有已定义的自己类型的静态只读实例(以 TestClass 为例,它定义了 3 个自己类型的静态只读实例)。
我想要这种自动化,因为我想遍历定义的类型,而不是冒险忘记将新实例添加到 All
的列表中。
好的,我让它工作了,这不是重点。但是为什么从静态构造函数调用时 FillAll
不起作用?请参阅 DefinedInstancesBase<T>
代码中注释的静态构造函数。我的意思是静态构造函数中的 FieldInfo.GetValue(null)
returns null,尽管调试器已经在调用 FieldInfo.GetValue(null)
之前创建了静态只读实例。
我很好奇为什么它不起作用。这是设计使然吗?
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;
}
}
}
这里有两个不同的问题。
- 上面代码的
static
构造函数中有错字。尝试将 static DefinedInstances()
更改为 static DefinedInstancesBase()
,因为目前它只是指定为私有静态函数。
- 第二个也是更重要的问题是了解调用各种构造函数的顺序。发生的事情是基础抽象 class 上的静态构造函数被实例化触发(在派生 class 中
First
字段的成员初始值设定项)。因此,当 DefinedInstancesBase
class 的 static
构造函数被调用时 First
仍然为空(因此调用 FindAll()
方法)。
查看以下代码(稍作修改以更好地说明问题)并输出:
public void Main()
{
DefinedInstancesTest dit = new DefinedInstancesTest();
dit.StaticReadOnlyInstancesAreEnumerated();
}
public abstract class DefinedInstancesBase<T>
{
public static IList<T> All
{
get
{
//if (_All == null)
// FillAll();
return _All;
}
}
// correctly named static ctor
static DefinedInstancesBase() { FillAll(); }
private static void FillAll()
{
Console.WriteLine("FillAll() called...");
var typeOfT = typeof(T);
var fields = typeOfT.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.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);
if (expectedClasses[i].Id != actualClasses[i].Id)
Console.WriteLine("not equal!");
}
}
private class TestClass : DefinedInstancesBase<TestClass>
{
public static readonly TestClass First;
public static readonly TestClass Second;
public static readonly TestClass Third;
public int Id { get; private set; }
static TestClass()
{
Console.WriteLine("TestClass() static ctor called...");
First = new TestClass(1);
Second = new TestClass(2);
Third = new TestClass(3);
}
private TestClass(int pId)
{
Console.WriteLine("TestClass({0}) instance ctor called...", pId);
Id = pId;
}
}
}
TestClass() static ctor called...
// the line "First = new TestClass(1);" now triggers the base class static ctor to be called,
// but the fields First, Second, and Third are all still equal to null at this point!
FillAll() called...
TestClass(1) instance ctor called...
TestClass(2) instance ctor called...
TestClass(3) instance ctor called...
// this null reference exception to be expected because the field value actually was null when FindAll() added it to the list
Unhandled Expecption:
System.NullReferenceException: Object reference not set to an instance of an object.
查看下面的代码。我想要一个 class 自动枚举所有已定义的自己类型的静态只读实例(以 TestClass 为例,它定义了 3 个自己类型的静态只读实例)。
我想要这种自动化,因为我想遍历定义的类型,而不是冒险忘记将新实例添加到 All
的列表中。
好的,我让它工作了,这不是重点。但是为什么从静态构造函数调用时 FillAll
不起作用?请参阅 DefinedInstancesBase<T>
代码中注释的静态构造函数。我的意思是静态构造函数中的 FieldInfo.GetValue(null)
returns null,尽管调试器已经在调用 FieldInfo.GetValue(null)
之前创建了静态只读实例。
我很好奇为什么它不起作用。这是设计使然吗?
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;
}
}
}
这里有两个不同的问题。
- 上面代码的
static
构造函数中有错字。尝试将static DefinedInstances()
更改为static DefinedInstancesBase()
,因为目前它只是指定为私有静态函数。 - 第二个也是更重要的问题是了解调用各种构造函数的顺序。发生的事情是基础抽象 class 上的静态构造函数被实例化触发(在派生 class 中
First
字段的成员初始值设定项)。因此,当DefinedInstancesBase
class 的static
构造函数被调用时First
仍然为空(因此调用FindAll()
方法)。
查看以下代码(稍作修改以更好地说明问题)并输出:
public void Main()
{
DefinedInstancesTest dit = new DefinedInstancesTest();
dit.StaticReadOnlyInstancesAreEnumerated();
}
public abstract class DefinedInstancesBase<T>
{
public static IList<T> All
{
get
{
//if (_All == null)
// FillAll();
return _All;
}
}
// correctly named static ctor
static DefinedInstancesBase() { FillAll(); }
private static void FillAll()
{
Console.WriteLine("FillAll() called...");
var typeOfT = typeof(T);
var fields = typeOfT.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.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);
if (expectedClasses[i].Id != actualClasses[i].Id)
Console.WriteLine("not equal!");
}
}
private class TestClass : DefinedInstancesBase<TestClass>
{
public static readonly TestClass First;
public static readonly TestClass Second;
public static readonly TestClass Third;
public int Id { get; private set; }
static TestClass()
{
Console.WriteLine("TestClass() static ctor called...");
First = new TestClass(1);
Second = new TestClass(2);
Third = new TestClass(3);
}
private TestClass(int pId)
{
Console.WriteLine("TestClass({0}) instance ctor called...", pId);
Id = pId;
}
}
}
TestClass() static ctor called...
// the line "First = new TestClass(1);" now triggers the base class static ctor to be called,
// but the fields First, Second, and Third are all still equal to null at this point!
FillAll() called...
TestClass(1) instance ctor called...
TestClass(2) instance ctor called...
TestClass(3) instance ctor called...
// this null reference exception to be expected because the field value actually was null when FindAll() added it to the list
Unhandled Expecption:
System.NullReferenceException: Object reference not set to an instance of an object.