我有一个类的基类,可以作为中央系统的插件。我想允许在第三方程序集中定义这些插件。中央系统根据一些命令行参数实例化插件。
为实现这一目标,我创建了一个可以使用这些类进行修饰的属性,如:
[ArgName("some-module")]
在初始化时,我使用一段反映所有已加载类型的代码,尝试找到具有该属性和正确参数的代码:
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.SingleOrDefault(x => /* check for attribute and predicate */);
这很好用。
但我觉得我正在重新发明DI框架本身可能拥有的东西。所以,既然我已经在我的项目中使用Ninject来处理其他一些依赖项,我想知道:有没有办法可以将这个职责委托给Ninject,而不是编写自定义反射代码?
换句话说,Ninject是否已经有一些我错过的内容大致如下:
kernel.Bind<ModuleBase>()
.ToTypesThatHaveThisAttribute<ArgName>()
.With(x => x.Name == userProvidedCommandlineArgument);
当然,我知道我可以为BindingToSyntax<T>
创建上述扩展方法,并在使用它时使语法更好。但是如果Ninject内置了这个功能,我想完全放弃反射代码。
答案 0 :(得分:2)
使用Ninject.Extensions.Conventions
你可以实现这样的目标(未经测试)。
string arg = null;
Kernel.Bind(x =>
{
x.FromThisAssembly()
.Select(t =>
{
var attributes = t.GetCustomAttributes(typeof(ArgNameAttribute));
if (attributes.Length == 0) return false;
var attribute = attributes[0];
return attribute.ArgName == arg;
})
.BindSelection((type, interfaces) => new[] {typeof(PluginBase)});
});
但说实话,与手动类型选择和与Reflection的绑定相比,它的代码量几乎相同,类似于您所写的内容。我的意思是这样的。
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => {
var attributes = t.GetCustomAttributes(typeof(ArgNameAttribute));
if (attributes.Length == 0) return false;
var attribute = attributes[0];
return attribute.ArgName == arg;
})
.Select(x => Kernel.Bind<PluginBase>().To(x))...
我可能会在没有Conventions
扩展名的情况下执行此操作,除非您发现它在其他方面有用。
请注意,这些代码都没有经过测试,仅举例说明。