C# 如何延迟属性内的静态初始化
我创建了一个类,它是一个(依赖注入)工厂和一个(依赖注入)工厂的交叉。称之为“Mono工厂”,它可以工作,看起来是这样的:C# 如何延迟属性内的静态初始化,c#,.net,singleton,factory,C#,.net,Singleton,Factory,我创建了一个类,它是一个(依赖注入)工厂和一个(依赖注入)工厂的交叉。称之为“Mono工厂”,它可以工作,看起来是这样的: public static class Context { public static BaseLogger LogObject = null; public static BaseLogger Log { get { return LogFactory.instance; }
public static class Context
{
public static BaseLogger LogObject = null;
public static BaseLogger Log
{
get
{
return LogFactory.instance;
}
}
class LogFactory
{
static LogFactory() { }
internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null);
}
}
//USAGE EXAMPLE:
//Optional initialization, done once when the application launches...
Context.LogObject = new ConLogger();
//Example invocation used throughout the rest of code...
Context.Log.Write("hello", LogSeverity.Information);
public static class Context
{
private static BaseLogger LogObject = null;
public static BaseLogger Log
{
get
{
return LogFactory.instance;
}
set
{
LogObject = value;
}
}
class LogFactory
{
static LogFactory() { }
internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null);
}
}
其想法是,mono工厂可以扩展到处理多个项目(例如,多个记录器)。但我本想让mono工厂看起来像这样:
public static class Context
{
public static BaseLogger LogObject = null;
public static BaseLogger Log
{
get
{
return LogFactory.instance;
}
}
class LogFactory
{
static LogFactory() { }
internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null);
}
}
//USAGE EXAMPLE:
//Optional initialization, done once when the application launches...
Context.LogObject = new ConLogger();
//Example invocation used throughout the rest of code...
Context.Log.Write("hello", LogSeverity.Information);
public static class Context
{
private static BaseLogger LogObject = null;
public static BaseLogger Log
{
get
{
return LogFactory.instance;
}
set
{
LogObject = value;
}
}
class LogFactory
{
static LogFactory() { }
internal static readonly BaseLogger instance = LogObject ?? new BaseLogger(null, null, null);
}
}
上面的方法不起作用,因为当触及Log属性时(通过setter调用),它会导致执行与getter相关的代码路径……这意味着内部LogFactory“实例”数据总是被设置为BaseLogger(设置“LogObject”总是太晚了!)
那么,在调用设置的路径时,是否有一种装饰或其他技巧会导致Log属性的“get”路径变慢?一些提示:
退房
我避免使用静态初始化。这在实践中可能会导致奇怪的问题。例如,如果您正在构建的内容抛出错误,那么windows加载程序将告诉您有问题,但不会告诉您问题所在。您的代码从未被实际调用,因此您没有机会出现异常来处理问题。我在第一次使用时构造第一个实例。下面是一个例子:
private static OrderCompletion instance;
/// <summary>
/// Get the single instance of the object
/// </summary>
public static OrderCompletion Instance
{
get
{
lock (typeof(OrderCompletion))
{
if (instance == null)
instance = new OrderCompletion();
}
return instance;
}
}
私有静态OrderCompletion实例;
///
///获取对象的单个实例
///
公共静态OrderCompletion实例
{
得到
{
锁(类型(订单完成))
{
if(实例==null)
实例=新订单完成();
}
返回实例;
}
}
一些提示:
退房
我避免使用静态初始化。这在实践中可能会导致奇怪的问题。例如,如果您正在构建的内容抛出错误,那么windows加载程序将告诉您有问题,但不会告诉您问题所在。您的代码从未被实际调用,因此您没有机会出现异常来处理问题。我在第一次使用时构造第一个实例。下面是一个例子:
private static OrderCompletion instance;
/// <summary>
/// Get the single instance of the object
/// </summary>
public static OrderCompletion Instance
{
get
{
lock (typeof(OrderCompletion))
{
if (instance == null)
instance = new OrderCompletion();
}
return instance;
}
}
私有静态OrderCompletion实例;
///
///获取对象的单个实例
///
公共静态OrderCompletion实例
{
得到
{
锁(类型(订单完成))
{
if(实例==null)
实例=新订单完成();
}
返回实例;
}
}
注意:这是对原始答案的完全重写;然而,这项建议仍然有效
第一:确保您没有在调试器下运行。例如,手表窗口可能会触及您的公共静态属性。这可能是第二个示例与第一个示例行为不同的原因之一。这听起来可能很傻,但你永远不知道
在.NET4下,您的第二个示例确实有效,我真诚地希望它在.NET2下也有效。只要不无意中触摸Context.Log
属性或LogFactory.instance
字段。然而,它看起来非常脆弱
此外,严格地说,您试图在这里使用的beforefieldinit
微妙之处可能会在多线程应用程序中影响您:LogFactory
的init不需要与Context.Log[Object]
的setter运行在同一线程上。这意味着当初始化LogFactory.instance
时,该线程上的Context.LogObject
还不需要设置,而另一个线程上的LogObject(此类同步可以延迟进行)。所以它不是线程安全的。您可以尝试通过使Context.LogObject
易失性来修复此问题,这样可以一次在所有线程上看到集合。但谁知道下一步我们会遇到什么样的比赛条件呢
在所有这些技巧之后,您仍然会得到以下相当不直观的结果:
Context.Log = value1; // OK
Context.Log = value2; // IGNORED
您希望setter的第二次调用可以工作(Context.Log==value2
)或抛出。不要被默默忽视
你也可以去
public static class Context
{
private static BaseLogger LogObject;
public static BaseLogger Log
{
get { return LogObject ?? LogFactory.instance; }
set { LogObject = value; }
}
private class LogFactory
{
static LogFactory() {}
internal static readonly BaseLogger instance
= new BaseLogger(null, null, null);
}
}
在这里,结果是有保证的,并且是懒惰的(与Jon Skeet的第五个单例方法一致)。而且它看起来更干净了。注意:这是对原始答案的完全重写;然而,这项建议仍然有效
第一:确保您没有在调试器下运行。例如,手表窗口可能会触及您的公共静态属性。这可能是第二个示例与第一个示例行为不同的原因之一。这听起来可能很傻,但你永远不知道
在.NET4下,您的第二个示例确实有效,我真诚地希望它在.NET2下也有效。只要不无意中触摸Context.Log
属性或LogFactory.instance
字段。然而,它看起来非常脆弱
此外,严格地说,您试图在这里使用的beforefieldinit
微妙之处可能会在多线程应用程序中影响您:LogFactory
的init不需要与Context.Log[Object]
的setter运行在同一线程上。这意味着当初始化LogFactory.instance
时,该线程上的Context.LogObject
还不需要设置,而另一个线程上的LogObject(此类同步可以延迟进行)。所以它不是线程安全的。您可以尝试通过使Context.LogObject
易失性来修复此问题,这样可以一次在所有线程上看到集合。但谁知道下一步我们会遇到什么样的比赛条件呢
在所有这些技巧之后,您仍然会得到以下相当不直观的结果:
Context.Log = value1; // OK
Context.Log = value2; // IGNORED
您希望setter的第二次调用可以工作(Context.Log==value2
)或抛出。不要被默默忽视
你也可以去
public static class Context
{
private static BaseLogger LogObject;
public static BaseLogger Log
{
get { return LogObject ?? LogFactory.instance; }
set { LogObject = value; }
}
private class LogFactory
{
static LogFactory() {}
internal static readonly BaseLogger instance
= new BaseLogger(null, null, null);
}
}
在这里,结果是有保证的,而且是懒惰的(与Jon Skeet的第五种单例冰毒一致)