c# – 来自静态单例内联初始化的NullReferenceException
根据
this question,应该保证我使用的静态字段被初始化:
10.4.5.1静态字段初始化:
我遇到了一个奇怪的情况,这似乎不是真的.我有两个类彼此循环依赖,并抛出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:
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(); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |