1

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

我想要这种自动化,因为我想循环定义的类型,而不是冒忘记将新实例添加到All.

好的,我有它的工作,这不是重点。但是为什么FillAll从静态构造函数调用时不起作用?请参阅代码中注释的静态构造函数DefinedInstancesBase<T>。我的意思是在静态构造函数中返回 null,尽管调试器在调用FieldInfo.GetValue(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;
        }
    }
}
4

1 回答 1

2

这里有两个不同的问题。

  1. static上面代码中的构造函数中有一个错字。尝试更改static DefinedInstances()static DefinedInstancesBase(),因为目前它只是指定为私有静态函数。
  2. 第二个也是更重要的问题是了解调用各种构造函数的顺序。发生的情况是基抽象类上的静态构造函数被First派生字段的实例化(在成员初始化程序期间)触发班级。因此,当调用类的构造函数(以及方法)First时,仍然为 null 。staticDefinedInstancesBaseFindAll()

请参阅以下代码(稍作修改以更好地说明问题)和输出:

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.
于 2015-06-24T12:09:54.403 回答