【发布时间】:2017-06-15 16:32:53
【问题描述】:
根据this question,应该保证我使用的静态字段被初始化:
10.4.5.1静态字段初始化:
类的静态字段变量初始化器对应于 以文本顺序执行的任务序列 它们出现在类声明中。如果是静态构造函数 (10.11节)存在于类中,执行静态字段 初始化器在执行该静态之前立即发生 构造函数。否则,静态字段初始化器在 在第一次使用静态之前的依赖于实现的时间 该类的字段。
我遇到了一个奇怪的情况,这似乎不是真的。我有两个类,它们相互之间存在循环依赖关系,并且抛出了 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 中的建议取消注释静态构造函数,问题仍然存在。使用此代码获取TypeInitializationException 与NullRefernceException 作为InnerException 在Synchronize SessionManager.GetInstance().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 ......
我知道这里存在某种循环依赖(在原始代码中并不那么明显),但我仍然不明白为什么代码无法初始化单例。除了避免循环依赖之外,这个用例的最佳方法是什么?
【问题讨论】:
-
很奇怪,我本来希望看到
StackOverflowException。在构造函数期间做任何过于复杂的事情通常不是一个好主意,特别是当每个都是单例并且每个最终都在其中一个完全完成初始化之前调用另一个时。也许看看延迟初始化? -
如果你看这个:
private static SessionManager _instance = new SessionManager(),它有两个重要步骤。 1.- 初始化 (new SessionManager()) 和 2 分配 (_instance = the obj)。如果您尝试在分配之前使用_instance(就像您所做的那样),则它为空。它会破坏你的 NPE。
标签: c# static initialization singleton