在类实例化之后在运行时使用依赖注入

时间:2012-03-22 20:54:38

标签: c# .net design-patterns tdd

我认为如果没有完整的架构更改,这可能是不可能的,但我有类(插件),可以在运行时为用户公开可配置的选项。

程序启动,用户可以选择使用各种插件,用户面对可以为每个插件修改的选项,用户按下播放按钮,这些配置选项可以读取和关闭。

当用户选择他们想要使用的插件时,会创建该类的实例,以便它可以显示该插件的可用选项。显然在正常情况下,当实例化类时,会传入构造函数参数。在这种情况下,如果插件具有ILogger的构造函数参数,并且其具体类具有DirectoryPath的构造函数参数,但这只有在用户单击播放时才可读,我怎么能得到我的IOC容器传递给它?

public class MyPlugin
{ 
  private ILogger Logger;
  private Dictionary<string, object> Properties = new Dictionary<string, object>();

  public MyPlugin(ILogger Logger)
  {
     this.Logger = Logger;
     SetupProperties();
  }

  private SetupProperties()
  {
    Properties.Add("LogDirectory","Please enter a directory");
  }

  private void UserPressedPlay()
  {
     //We can now read the property values user has entered
     //Now I want to instantiate FileLogger with the LogDirectory property
  }

  public DoSomething(string Data)
  {
    this.Logger.Write(Data);
  }
}

public interface ILogger
{
  void Write(string Data);
}

public FileLogger : ILogger
{
  private string DirectoryPath;

  public FileLogger(string Directory)
  {
    this.DirectoryPath = Directory;
  }

  public Write(string Data)
  { 
      //Write to file
  }
}

4 个答案:

答案 0 :(得分:0)

您可以向DirectoryPath添加FileLogger媒体资源,稍后再进行初始化。

public FileLogger : ILogger
{
    public string DirectoryPath { get; set; }

    public Write(string Data)
    {
        if (DirectoryPath != null) {
            // Write to file
        }
    } 
}

答案 1 :(得分:0)

您可以使用Expression.New使用上述参数动态创建/调用构造函数。

    Func<String, FileLogger> func = GetConsturctor<String, FileLogger>(); // Cache it somewhere.

    FileLogger logger = func(@"C:\Loggerpath\"); // instantiate

   static Func<TArg, TRes> GetConsturctor<TArg, TRes>()
    {
        Type resultType = typeof(TRes);
        Type argType = typeof(TArg);

        Type[] argumentArray = new Type[] { argType };
        ConstructorInfo constructor = resultType.GetConstructor(argumentArray);

        ParameterExpression param = Expression.Parameter(argType);
        Expression<Func<TArg, TRes>> result = Expression.Lambda<Func<TArg, TRes>>(
        Expression.New(constructor, param), param);
        return result.Compile();
    }

答案 2 :(得分:0)

所以DI提供了很大的灵活性和较低的耦合,但通常没有必要。对于这种特殊情况,担心如何添加它可能是过度的。看来你在这里有一个GUI配置为匹配你用来配置它的FileLogger类,所以这里的DI可能有点过分。你有这个插件今天使用的任何其他类型的ILogger吗?如果是这样,工厂选项可能是一个好选择。我怀疑,基于提到的GUI,只有一种类型的记录器。在这种情况下,不要挂在这里使用DI。如果你想弄清楚如何测试MyPlugin课程,并且不确定如何在没有DI的情况下这样做,那么请告诉我,我将扩展这个答案以展示如何完成它。

希望有所帮助!

布兰登

答案 3 :(得分:0)

你想要做的是传入ILogger工厂而不是实际的ILogger。然后可以使用用户的参数从UserPressedPlay调用工厂以创建实际的Logger实例。