10.4.5.1静态字段初始化:
The static field variable initializers of a class correspond to a
sequence of assignments that are executed in the textual order in
which they appear in the class declaration. If a static constructor
(Section 10.11) exists in the class,execution of the static field
initializers occurs immediately prior to executing that static
constructor. Otherwise,the static field initializers are executed at
an implementation-dependent time prior to the first use of a static
field of that class.
我遇到了一个奇怪的情况,这似乎不是真的.我有两个类彼此循环依赖,并抛出NullReferenceException.
我能够在以下简化的示例中重现此问题,看看:
public class SessionManager { //// static constructor doesn't matter //static SessionManager() //{ // _instance = new SessionManager(); //} private static SessionManager _instance = new SessionManager(); public static SessionManager GetInstance() { return _instance; } public SessionManager() { Console.WriteLine($"{nameof(SessionManager)} constructor called"); this.RecoverState(); } public bool RecoverState() { Console.WriteLine($"{nameof(RecoverState)} called"); List<SessionInfo> activeSessionsInDb = SessionManagerDatabase.GetInstance().LoadActiveSessionsFromDb(); // ... return true; } public List<SessionInfo> GetAllActiveSessions() { Console.WriteLine($"{nameof(GetAllActiveSessions)} called"); return new List<SessionInfo>(); } } public class SessionManagerDatabase { //// static constructor doesn't matter //static SessionManagerDatabase() //{ // _instance = new SessionManagerDatabase(); //} private static readonly SessionManagerDatabase _instance = new SessionManagerDatabase(); public static SessionManagerDatabase GetInstance() { return _instance; } public SessionManagerDatabase() { Console.WriteLine($"{nameof(SessionManagerDatabase)} constructor called"); Synchronize(); } public void Synchronize() { Console.WriteLine($"{nameof(Synchronize)} called"); // NullReferenceException here List<SessionInfo> memorySessions = SessionManager.GetInstance().GetAllActiveSessions(); //... } public List<SessionInfo> LoadActiveSessionsFromDb() { Console.WriteLine($"{nameof(LoadActiveSessionsFromDb)} called"); return new List<SessionInfo>(); } } public class SessionInfo { }
如果您按照其他question中的建议取消注释静态构造函数,问题仍然存在.使用此代码在SessionManager.GetInstance()的Synchronize中使用NullRefernceException作为InnerException获取TypeInitializationException.GetAllActiveSessions():
static void Main(string[] args) { try { var sessionManagerInstance = SessionManager.GetInstance(); } catch (TypeInitializationException e) { Console.WriteLine(e); throw; } }
控制台输出:
SessionManager constructor called RecoverState called SessionManagerDatabase constructor called Synchronize called System.TypeInitializationException: Der Typeninitialisierer für "SessionManager" hat eine Ausnahme verursacht. ---> System.TypeInitializationException: Der Typeninitialisierer für "SessionManagerDatabase" hat eine Ausnahme verursacht. ---> System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei ConsoleApplication_CSharp.Program.SessionManagerDatabase.Synchronize() in ...... bei ConsoleApplication_CSharp.Program.SessionManagerDatabase..ctor() in ...... bei ConsoleApplication_CSharp.Program.SessionManagerDatabase..cctor() in ...... --- Ende der internen Ausnahmestapelüberwachung --- bei ConsoleApplication_CSharp.Program.SessionManagerDatabase.GetInstance() bei ConsoleApplication_CSharp.Program.SessionManager.RecoverState() in ...... bei ConsoleApplication_CSharp.Program.SessionManager..ctor() in ..... bei ConsoleApplication_CSharp.Program.SessionManager..cctor() in ...... --- Ende der internen Ausnahmestapelüberwachung --- bei ConsoleApplication_CSharp.Program.SessionManager.GetInstance() bei ConsoleApplication_CSharp.Program.Main(String[] args) in ......
我知道这里存在某种循环依赖(在原始代码中不那么明显),但我仍然不明白为什么代码无法初始化单例.除了避免循环依赖之外,这个用例的最佳方法是什么?
解决方法
IL_0001: newobj instance void SO.Program/SessionManager::.ctor() IL_0006: stsfld class SO.Program/SessionManager SO.Program/SessionManager::_instance
在这里,您可以看到对静态构造函数的调用有两个步骤.它首先初始化一个新实例,然后分配它.这意味着当你进行依赖于实例存在的跨类调用时,你就会陷入困境.它仍然在创建实例的过程中.之后可以调用它.
您可以通过创建静态Initialize方法来完成此操作,该方法执行即时调用.
试试这个:
static SessionManager() { _instance = new SessionManager(); _instance.RecoverState(); } static SessionManagerDatabase() { _instance = new SessionManagerDatabase(); _instance.Synchronize(); }