【发布时间】:2021-11-12 03:16:05
【问题描述】:
根据Parameterized Instantiation (Func<X, Y, B>) 文档:
但是,如果将一个对象注册为 SingleInstance() 并多次调用 Func
来解析该对象,则无论传入不同的参数,每次都会得到相同的对象实例. 只是传递不同的参数不会破坏对生命周期范围的尊重。
具有不同参数构造函数的单例类如何工作?
【问题讨论】:
根据Parameterized Instantiation (Func<X, Y, B>) 文档:
但是,如果将一个对象注册为 SingleInstance() 并多次调用 Func
来解析该对象,则无论传入不同的参数,每次都会得到相同的对象实例. 只是传递不同的参数不会破坏对生命周期范围的尊重。
具有不同参数构造函数的单例类如何工作?
【问题讨论】:
Autofac 工厂注册不会缓存每个参数的实例。
让我们尝试以下类型:
public class Foo
{
public Foo(Int32 a)
{
this._a = a;
}
private readonly Int32 _a;
public override String ToString()
{
return String.Format("a={0}", this._a);
}
}
如果你这样注册:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf().SingleInstance();
Autofac 将始终注册单个实例,无论您如何解决它(即使您使用 Func 工厂):
Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1 = foo1Factory(1);
Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo2 = foo2Factory(2);
foo1 和 foo2 将是同一个实例。
如果您希望每个参数只有一个实例,这意味着您需要多个Foo 实例,因此您不能使用SingleInstance() 方法注册Foo。您将必须实现一个自定义工厂,该工厂将进行缓存以使每个参数都有一个实例:
public class FooFactory
{
public FooFactory(IComponentContext componentContext)
{
this._componentContext = componentContext;
this._instances = new Dictionary<Int32, Foo>();
this._lock = new Object();
}
private readonly IComponentContext _componentContext;
private readonly Dictionary<Int32, Foo> _instances;
private readonly Object _lock;
public Foo GetInstance(Int32 a)
{
Foo parameterizedFoo;
if (!this._instances.TryGetValue(a, out parameterizedFoo))
{
lock (this._lock)
{
if (!this._instances.TryGetValue(a, out parameterizedFoo))
{
Parameter aParameter = new TypedParameter(typeof(Int32), a);
parameterizedFoo = this._componentContext
.Resolve<Foo>(aParameter);
this._instances[a] = parameterizedFoo;
}
}
}
return parameterizedFoo;
}
}
你可以这样注册:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf();
builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
使用下面的代码来使用它:
FooFactory fooFactory = scope.Resolve<FooFactory>();
Foo foo = fooFactory.GetInstance(1);
但是如果你想使用Func<Int32, Foo>而不是FooFactory.GetInstance,你可以将方法注册为Func<Int32, Foo>:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf();
builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
builder.Register(c => new Func<Int32, Foo>(c.Resolve<FooFactory>().GetInstance))
.As<Func<Int32, Foo>>()
.SingleInstance();
您现在可以使用此代码:
Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1 = foo1Factory(1);
Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
Foo foo2 = foo2Factory(2);
Func<Int32, Foo> foo1PrimeFactory = scope.Resolve<Func<Int32, Foo>>();
Foo foo1Prime = foo1PrimeFactory(1);
foo1 和 foo2 将不同,而 foo1Prime 将与 foo1 相同。
【讨论】:
最后,我的同事也有类似的方法
public interface IKey
{
}
public interface IKeyed<out T>
{
T this[IKey key] { get; }
}
public class Keyed<T> : IKeyed<T>
{
private ConcurrentDictionary<IKey, T> _dict = new ConcurrentDictionary<IKey,T>();
T IKeyed<T>.this[IKey key]
{
get
{
return _dict.GetOrAdd(key, k => IoC.Resolve<T>(new TypedParameter(key.GetType(), key)));
}
}
}
在应用启动中,注册
_builder.RegisterGeneric(typeof(Keyed<>))
.As(typeof(IKeyed<>))
.SingleInstance();
【讨论】: