簡體   English   中英

為什么FieldInfo.GetValue(null)在靜態構造函數中不起作用

[英]Why is FieldInfo.GetValue(null) not working in static constructor

請參見下面的代碼。 我想要一個能夠自動枚舉其自身類型的所有已定義靜態只讀實例的類(例如,請參見TestClass,它定義了其自身類型的3個靜態只讀實例)。

我想要這種自動化,因為我想遍歷定義的類型,而不必冒險忘記將新實例添加到All列表中而進行更改。

好吧,我已經開始工作了,那不是重點。 但是為什么從靜態構造函數調用FillAll不起作用? 請參見DefinedInstancesBase<T>代碼中DefinedInstancesBase<T>注釋的靜態構造函數。 我的意思是,盡管調試器已經在調用FieldInfo.GetValue(null)之前創建了靜態只讀實例,但FieldInfo.GetValue(null)在靜態構造函數中返回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;
        }
    }
}

這里有兩個單獨的問題在起作用。

  1. 上面的代碼中的static構造函數中有一個錯字。 嘗試將static DefinedInstances()更改為static DefinedInstancesBase() ,因為當前它只是被指定為私有靜態函數。
  2. 第二個也是更重要的問題是要了解各種構造函數的調用順序。發生的情況是,基本抽象類上的靜態構造函數是由(在成員初始化程序中) First字段的實例化觸發的。派生類。 因此,在調用DefinedInstancesBase類的static構造函數(並因此調用FindAll()方法)時, First仍然為null。

請參見以下代碼(稍作修改以更好地說明問題)並輸出:

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.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM