C# Singleton范围内的Ninject Memoize实例
我使用Ninject实例化一些传递了构造函数arg的对象,例如:C# Singleton范围内的Ninject Memoize实例,c#,dependency-injection,inversion-of-control,ninject,garbage-collector,C#,Dependency Injection,Inversion Of Control,Ninject,Garbage Collector,我使用Ninject实例化一些传递了构造函数arg的对象,例如: class MyClass { public MyClass(string myArg) { this.myArg = myArg; } } 我需要的这个类的实例数在运行时之前是未知的,但我要做的是确保myArg的每个变体都会产生不同的单例实例(因此两次请求相同的值会返回相同的实例,但不同的arg会返回不同的实例) 有人知道这样做的好方法吗,最好是内置的 我发现一篇文章是为旧版本的Ninj
class MyClass
{
public MyClass(string myArg)
{
this.myArg = myArg;
}
}
我需要的这个类的实例数在运行时之前是未知的,但我要做的是确保myArg
的每个变体都会产生不同的单例实例(因此两次请求相同的值会返回相同的实例,但不同的arg会返回不同的实例)
有人知道这样做的好方法吗,最好是内置的
我发现一篇文章是为旧版本的Ninject写的,但我希望新版本能有一个更整洁的解决方案
编辑
以下是我的答案,改编自阿基姆的回答:
private readonly ConcurrentBag<string> scopeParameters = new ConcurrentBag<string>();
internal object ParameterScope(IContext context, string parameterName)
{
var param = context.Parameters.First(p => p.Name.Equals(parameterName));
var paramValue = param.GetValue(context, context.Request.Target) as string;
paramValue = string.Intern(paramValue);
if (paramValue != null && !scopeParameters.Contains(paramValue))
{
scopeParameters.Add(paramValue);
}
return paramValue;
}
public override void Load()
{
Bind<MyClass>()
.ToSelf()
.InScope(c => ParameterScope(c, "myArg"));
Bind<IMyClassFactory>()
.ToFactory();
}
private readonly ConcurrentBag scopeParameters=new ConcurrentBag();
内部对象参数范围(IContext上下文,字符串参数名称)
{
var param=context.Parameters.First(p=>p.Name.Equals(parameterName));
var paramValue=param.GetValue(context,context.Request.Target)作为字符串;
paramValue=string.Intern(paramValue);
if(paramValue!=null&&!scopeParameters.Contains(paramValue))
{
scopeParameters.Add(paramValue);
}
返回参数值;
}
公共覆盖无效负载()
{
绑定()
.ToSelf()
.InScope(c=>参数范围(c,“myArg”);
绑定()
.ToFactory();
}
您可以通过使用IBindingNamedWithOrOnSyntax InScope(Func scope)
方法为MyClass
绑定提供自定义作用域来实现require行为
指示应重新使用通过绑定激活的实例
只要所提供回调返回的对象保持活动状态
(即,未被垃圾回收)
所以,您需要从Func scope
返回第一个构造函数参数的值,并确保不会收集它
以下是一个片段:
public class Module : NinjectModule
{
// stores string myArg to protect from CG
ConcurrentBag<string> ParamSet = new ConcurrentBag<string>();
public override void Load()
{
Bind<MyClass>()
.ToSelf()
// custom scope
.InScope((context) =>
{
// get first constructor argument
var param = context.Parameters.First().GetValue(context, context.Request.Target) as string;
// retrieves system reference to string
param = string.Intern(param);
// protect value from CG
if(param != null && ParamSet.Contains(param))
{
// protect from GC
ParamSet.Add(param);
}
// make Ninject to return same instance for this argument
return param;
});
}
}
公共类模块:Ninject模块
{
//存储字符串myArg以防止CG攻击
ConcurrentBag ParamSet=新ConcurrentBag();
公共覆盖无效负载()
{
绑定()
.ToSelf()
//自定义范围
.InScope((上下文)=>
{
//获取第一个构造函数参数
var param=context.Parameters.First().GetValue(context,context.Request.Target)作为字符串;
//检索对字符串的系统引用
param=string.Intern(param);
//保护CG的价值
if(param!=null&&ParamSet.Contains(param))
{
//保护免受GC攻击
ParamSet.Add(param);
}
//使Ninject返回此参数的相同实例
返回参数;
});
}
}
ps:我非常喜欢这个解决方案的简单性,我在维基上得到了一些额外的帮助。不过,这些实例需要来自工厂,您知道这是否与工厂扩展兼容吗?我想一定会的。它肯定能用!看,我在我的代码中整理了一下您的示例,以便告诉它使用哪个参数,而不是总是使用第一个参数,并且我编写了一些测试。非常好用,谢谢!!检查抽象工厂的自动实现