Singleton属性的.Net Lazy初始化
本文关键字:Lazy 初始化 Net 属性 Singleton | 更新日期: 2023-09-27 18:24:56
我有一个类DataAdapter
,它被我的站点实例化为单例对象。该类有一个Person属性,我希望它是一个懒惰的单例,实现这一点的代码:
private readonly object _personLock = new object();
private volatile IPersonManager _person;
public IPersonManager Person
{
get
{
if (_person == null)
{
lock (_personLock)
{
if (_person == null)
{
_person = new PersonManager(_adUserName, _adPassword, client);
}
}
}
return _person;
}
}
(PersonManager构造函数的这三个参数是当前对象上的属性/字段。)这段代码运行得很好(它是一个双锁检查模式)。
然而,这是很多代码,我想利用新的Lazy<>键入.Net 4.0以使其更简单。所以我把代码改成:
private static readonly Lazy<IPersonManager> _person = new Lazy<IPersonManager>(() => new PersonManager(_adUserName, _adPassword, client));
public static IPersonManager Person { get { return _person.Value; } }
但这不起作用,因为这三个参数不是静态的(它们是当前方法上的实例对象)。我发现的任何一篇文章都没有提到这一点。我需要某种方法将这些值传递到lambda表达式中?懒惰者<>类似乎期望一个空签名。
好吧,如果Lazy使用Instance属性来处理您的singleton实例,从而为自己提供属性,该怎么办?这些字段仍然可以是私有的,因为我们在类内部使用它们(聪明),并且在Singleton.Instance在执行过程中第一次被引用之前,整个过程仍然是惰性的。但是,在代码尝试获取Person属性之前,私有字段必须具有正确的值。如果它们在Singleton实例化时被急切地加载,那没关系。
借用C#In Depth,这里有一个准懒惰Singleton,它有一个完全懒惰的Person成员,使用引用Singleton的lambda初始化。
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}
private Singleton()
{
//I HIGHLY recommend you initialize _adusername,
//_adpassword and client here.
}
public static Singleton Instance
{
get
{
return instance;
}
}
private static readonly Lazy<IPersonManager> _person =
new Lazy<IPersonManager>(() => new PersonManager(Instance._adUserName, Instance._adPassword, Instance.client));
public static IPersonManager Person { get { return _person.Value; } }
private object _adUserName;
private object _adPassword;
private object client;
}
public class PersonManager:IPersonManager {}
public interface IPersonManager{}
编辑:如果您有IoC,请使用IoC。您当前正在尝试混合模式;您使用IoC使用运行时规则将实例类"提升"为singleton,但随后尝试基于此伪singleton的实例范围数据字段实例化编译器强制的惰性静态属性。这根本不起作用
一旦进入IoC,每个依赖项都应该被注册和注入。使用Ninject注册PersonManager作为IPersonManager实现,然后为您的主单例DataAdapter创建一个构造函数,该构造函数可以被赋予一个生成IPersonManager的Func。您通常可以为此定义一个自定义函数,在您的情况下,该函数将利用IoC从容器中保存的单个DataAdapter实例中提供所需的实例数据。
注意:这些数据字段现在必须是公开可读的,以避免一些严重丑陋的反映;您可以将字段定义为只读字段,也可以只获取属性以防止人们篡改它们,但您的消费者将能够看到它们。
第2版:以下是我的想法:
//in your Ninject bindings:
kernel.Bind<DataAdapter>().ToSelf().InSingletonScope();
kernel.Bind<PersonManager>().ToSelf().InSingletonScope();
//to bind the interface
kernel.Bind<IPersonManager>()
.ToMethod(c =>{
var adapter = kernel.Get<DataAdapter>();
//this is why these fields would have to be public
var arg1 = new ConstructorArgument("adUserName", adapter._adUserName)
var arg2 = new ConstructorArgument("adPassword", adapter._adPassword)
var arg3 = new ConstructorArgument("client", adapter.client)
//the names of the arguments must match PersonManager's constructor
c.Kernel.Get<PersonManager>(arg1, arg2, arg3);
});
//now in your DataAdapter, specify a constructor like this, and Ninject will provide:
public DataAdapter(Func<IPersonManager> personFunc)
{
//_person should obviously not be instantiated where it's defined in this case
_person = new Lazy<IPersonManager>(personFunc);
}
(PersonManager构造函数的这三个参数是当前对象上的属性/字段。)
构造函数用于初始化对象。此时,您试图传递的参数将没有赋值。如果您的对象需要正确初始化这些值,那么在初始化时需要传入这些值。
尽管您可以将属性转换为方法并将这些值传递给静态GetInstance方法,但您只会在第一次调用GetInstance时设置一次。这可能不是一个好主意,但它是可以做到的。我会将Person属性转换为Method,并接受这些参数并使用它们初始化构造函数。这确实意味着你不会使用Lazy<T>
,你的代码行会增加,但你会有更可预测的行为。