第二种方法
我有一个家庭的,提供一个可扩展的应用程序(即,不固定的)设置的,其可以通过各种插件中使用的变量。动态依赖注入
的例子是:
- 日志事件源
- 计算的结果源
- 对系统资源的使用
- 源的服务表现指标源
- ...
插件可以使用任何组合的SE。
样品插件可以是:
- 的定制错误记录器,使用1
- 定制的统计模块,使用2
- 使用3和4
我想达到的是
- presen t给出一组可用的插件列表(当没有日志事件源时,您不应该能够选择自定义错误记录器)。
- 得到一个简单而安全的方式来将变量传递给插件,以避免由于缺少变量而导致运行时错误。
奖励将允许插件可选地要求变量,例如,一个插件,需要4.并且可选地使用3.如果可用(但也可用其他)。
第一种方法
我想实现某种形式的“动态依赖注入”的。 让我用一个用例来解释它。
我正在构建一组将用于一系列应用程序的库。 每个应用程序都可以提供一组不同的变量,这些变量可用于某些需要这些变量的“处理程序”。 根据具体的可用变量,必须确定可用处理程序的数量,因为只有在处理程序可以访问所有必需的变量时才能使用处理程序。 此外我正在寻找一种方法来尽可能安全地进行调用。编译时间可能不可能,但“检查一次,之后再也不会失败”就可以。
下面是第一个草图。在这个阶段一切仍然可以改变。
class DynamicDependencyInjectionTest
{
private ISomeAlwaysPresentClass a;
private ISomeOptionalClass optionA;
private ISomeOtherOptionalClass optionB;
private ISomeMultipleOption[] multi;
private IDependentFunction dependentFunction;
void InvokeDependency()
{
// the number of available dependencies varies.
// some could be guaranteed, others are optional, some maybe have several instances
var availableDependencies = new IDependencyBase[] {a, optionA, optionB}.Concat(multi).ToArray();
//var availableDependencies = new IDependencyBase[] { a };
//var availableDependencies = new IDependencyBase[] { a, optionA }.ToArray();
//var availableDependencies = new IDependencyBase[] { a, optionB }.ToArray();
//var availableDependencies = new IDependencyBase[] { a , multi.First() };
//ToDo
// this is what I want to do
// since we checked it before, this must always succeed
somehowInvoke(dependentFunction, availableDependencies);
}
void SetDependentFunction(IDependentFunction dependentFunction)
{
if (! WeCanUseThisDependentFunction(dependentFunction))
throw new ArgumentException();
this.dependentFunction = dependentFunction;
}
private bool WeCanUseThisDependentFunction(IDependentFunction dependentFunction)
{
//ToDo
//check if we can fulfill the requested dependencies
return true;
}
/// <summary>
/// Provide a list which can be used by the user (e.g. selected from a combobox)
/// </summary>
IDependentFunction[] AllDependentFunctionsAvailableForThisApplication()
{
IDependentFunction[] allDependentFunctions = GetAllDependentFunctionsViaReflection();
return allDependentFunctions.Where(WeCanUseThisDependentFunction).ToArray();
}
/// <summary>
/// Returns all possible candidates
/// </summary>
private IDependentFunction[] GetAllDependentFunctionsViaReflection()
{
var types = Assembly.GetEntryAssembly()
.GetTypes()
.Where(t => t.IsClass && typeof (IDependentFunction).IsAssignableFrom(t))
.ToArray();
var instances = types.Select(t => Activator.CreateInstance(t) as IDependentFunction).ToArray();
return instances;
}
private void somehowInvoke(IDependentFunction dependentFunction, IDependencyBase[] availableDependencies)
{
//ToDo
}
}
// the interfaces may of course by changed!
/// <summary>
/// Requires a default constructor
/// </summary>
interface IDependentFunction
{
void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies);
Type[] RequiredDependencies { get; }
}
interface IDependencyBase { }
interface ISomeAlwaysPresentClass : IDependencyBase { }
interface ISomeOptionalClass : IDependencyBase { }
interface ISomeOtherOptionalClass : IDependencyBase { }
interface ISomeMultipleOption : IDependencyBase { }
class BasicDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
get { return new[] {typeof(ISomeAlwaysPresentClass)}; }
}
}
class AdvancedDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
get { return new[] { typeof(ISomeAlwaysPresentClass), typeof(ISomeOptionalClass) }; }
}
}
class MaximalDependentFunction : IDependentFunction
{
public void Invoke(ISomeAlwaysPresentClass a, IDependencyBase[] dependencies)
{
;
}
public Type[] RequiredDependencies
{
// note the array in the type of ISomeMultipleOption[]
get { return new[] { typeof(ISomeAlwaysPresentClass), typeof(ISomeOptionalClass), typeof(ISomeOtherOptionalClass), typeof(ISomeMultipleOption[]) }; }
}
}
正如马克的答案所暗示的,您应该尊重KISS原则,并且不要试图用一些模糊的DI重新发明轮子。想想那些会读你的代码的人。即使你在几个月内也会头疼,以便首先了解你的意图...... – Fab