C# 为什么FieldInfo.GetValue(null)不能在静态构造函数中工作

C# 为什么FieldInfo.GetValue(null)不能在静态构造函数中工作,c#,reflection,static-constructor,C#,Reflection,Static Constructor,请参阅下面的代码。我想要一个类,它自动枚举它自己类型的所有已定义的静态只读实例(例如,请参见TestClass,它定义了它自己类型的3个静态只读实例) 我希望实现这种自动化,因为我希望在定义的类型上循环,而不冒忘记将新实例添加到All列表的风险 好的,我让它工作了,这不是重点。但是为什么从静态构造函数调用时,FillAll不起作用呢?请参阅definedInstanceBasecode中注释的静态构造函数。我的意思是FieldInfo.GetValue(null)在静态构造函数中返回null,尽

请参阅下面的代码。我想要一个类,它自动枚举它自己类型的所有已定义的静态只读实例(例如,请参见TestClass,它定义了它自己类型的3个静态只读实例)

我希望实现这种自动化,因为我希望在定义的类型上循环,而不冒忘记将新实例添加到
All
列表的风险

好的,我让它工作了,这不是重点。但是为什么从静态构造函数调用时,
FillAll
不起作用呢?请参阅
definedInstanceBase
code中注释的静态构造函数。我的意思是
FieldInfo.GetValue(null)
在静态构造函数中返回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;
        }
    }
}
公共抽象类definedInstanceBase
{
公共静态IList All
{
得到
{
如果(_All==null)
{
FillAll();
}
全部归还;
}
}
//为什么这样不行?不知道。
//静态定义的stancesbase()
//{
//FillAll();
//}
私有静态void FillAll()
{
var typeOfT=typeof(T);
var fields=typeOfT.GetFields(BindingFlags.Public | BindingFlags.Static);
var fieldsOfTypeT=字段,其中(f=>f.FieldType==typeOfT);
_All=新列表();
foreach(变量fieldOfTypeT在FieldSoftTypet中)
{
_All.Add((T)fieldOfTypeT.GetValue(null));
}
}
私有静态列表_All=null;
}
[测试类]
公共类定义的状态测试
{
[测试方法]
public void StaticReadOnlyInstancesAreEnumerated()
{
//给定
var expectedClasses=新列表
{
TestClass。首先,
测试类。第二,
测试类。第三,
};
//什么时候
var actualClasses=TestClass.All;
//然后

对于(var i=0;i,这里有两个独立的问题

  • 上面代码中的
    static
    构造函数中存在键入错误。请尝试将
    static DefinedInstances()
    更改为
    static definedInstancebase()
    ,因为当前它只是指定为私有静态函数
  • 第二个也是更重要的问题是理解各种构造函数被调用的顺序。发生的是,基本抽象类上的静态构造函数被实例化触发(在成员初始化过程中)派生类中的
    First
    字段。因此,当调用
    definedInstanceBase
    类的
    static
    构造函数(以及
    FindAll()
    方法)时,
    First
    仍然为空
  • 请参阅以下代码(稍作修改以更好地说明问题)和输出:

    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.
    
    public void Main()
    {
    DefinedInstanceTest dit=新定义的InstanceTest();
    StaticReadOnlyInstancesAreEnumerated();
    }
    公共抽象类definedInstanceBase
    {
    公共静态IList All
    {
    得到
    {
    //如果(_All==null)
    //FillAll();
    全部归还;
    }
    }
    //正确命名的静态构造函数
    静态定义的StanceBase(){FillAll();}
    私有静态void FillAll()
    {
    WriteLine(“FillAll()调用…”);
    var typeOfT=typeof(T);
    var fields=typeOfT.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
    var fieldsOfTypeT=字段,其中(f=>f.FieldType==typeOfT);
    _All=新列表();
    foreach(变量fieldOfTypeT在FieldSoftTypet中)
    {
    _All.Add((T)fieldOfTypeT.GetValue(null));
    }
    }
    私有静态列表_All=null;
    }
    //[测试类]
    公共类定义的状态测试
    {
    //[测试方法]
    public void StaticReadOnlyInstancesAreEnumerated()
    {
    //给定
    var expectedClasses=新列表
    {
    TestClass。首先,
    测试类。第二,
    测试类。第三,
    };
    //什么时候
    var actualClasses=TestClass.All;
    //然后
    
    for(var i=0;iI不遵循..它编译。并且
    typeof(T)
    正在工作。你的意思是什么,你能详细说明一下吗?构造函数必须和它们的类同名,你的类不能。你的类叫做definedInstanceBase,但是(注释掉了)构造函数有一个不同的名称-它被称为DefinedInstances,也就是说,它实际上不是一个构造函数。哇,我的错。这是一个重命名,后来当静态构造函数已经成为一条注释时应用。所以这不是问题。所以本质上,试着通过调用
    FillAll()使
    TestMethod
    工作
    静态构造函数中
    不是延迟加载属性
    All
    @mikedklerk,它本身不是延迟加载问题。它是在
    第一个
    字段初始化为对象之前调用基类中的静态构造函数。因此该字段的当前值(在调用
    FindAll()
    时)正确返回为
    null