【问题标题】:Initializing objects using configuration file at runtime在运行时使用配置文件初始化对象
【发布时间】:2012-04-04 16:00:32
【问题描述】:

我目前正在编写一个项目,需要根据 XML 配置文件生成不同类型的对象。

生成的每个对象都是IProvidor 接口的实例,并且需要包含XML 配置文件定义的多个预处理和处理方法。 我生成了不同的工厂类:

  1. 创建 Provider(实现 IProvider 接口)
  2. 创建预处理操作(我有 IPreProcessor 接口,所有 preProcessor 类都需要实现。
  3. 处理方法也一样(IHandler 接口由多个类实现)。

问题

如何在运行时将所有这些组合到一个对象中?

【问题讨论】:

  • 这个问题很模糊。我不清楚你到底想知道什么。
  • 我正在寻找的是:
  • @Steven,我正在寻找的是:假设我有两个不同的提供程序 MailProvider 和 FTPProvider(都实现 IProvider)和两个不同的处理程序类 ZipFilesHandler 和 RARFileHandle(实现 IHandler)。从理论上讲,我有四个构建新对象的选项(带有 ZipFileHandler 功能的 MailProvider、带有 RARFileHandler 功能的 MailProvider、带有 ZipFileHandler 的 FTPProvider 等)如何在运行时生成正确的对象

标签: c# .net .net-4.0 configuration-files


【解决方案1】:

Olivier Jacot-Desc 绝对是在正确的轨道上(+1)。他的回答中唯一缺少的是从配置中加载正确的实现。

有很多方法可以做到这一点,例如将类型名称存储在配置中,但您也可以采用更简单的方法,例如在配置中存储一个简单的布尔值。

IProvider providerX = GetProviderFromConfig();
IHandler handlerZ = GetHandlerFromConfig();
IPreProcessor preProcessorY = GetProcessorFromConfig();

var provider = 
    new ProviderWrapper(providerX, preProcessorY, handlerZ);

private static IProvider GetProviderFromConfig()
{
    if (ConfigurationManager.AppSettings["provider"] == "mail")
    {
         return new MailProvider();
    }
    else
    {
         return new FtpProvider();
    }
}

// implement GetHandlerFromConfig  just like
// the GetProvider.

更新

当您有许多类型需要切换时,存储类型的名称可能是更好的选择:

private static IProvider GetProviderFromConfig()
{
    string typeName =
        ConfigurationManager.AppSettings["provider"];

    Type providerType = Type.GetType(typeName);

    return (IProvider)
        Activator.CreateInstance(providerType);
}

更新 2

这是一个如何使用 DI 容器进行配置的示例。我正在使用Simple Injector(与extensions),但任何容器都可以(尽管每个容器的配置方式不同):

注册:

using SimpleInjector;
using SimpleInjector.Extensions;

Type providerType = Type.GetType(
    ConfigurationManager.AppSettings["provider"]);

Type handlerType = Type.GetType(
    ConfigurationManager.AppSettings["handler"]);

Type processorType = Type.GetType(
    ConfigurationManager.AppSettings["preProcessor"]);

var container = new Container();

container.Register(typeof(IProvider), providerType);
container.Register(typeof(IHandler), handlerType);
container.Register(typeof(IPreProcessor), processorType);

解析提供者:

var provider = container.GetInstance<IPovider>();

提示:如果您使用构造函数注入,则不必手动连接类型,容器会为您完成此操作。例如,当您的 MailProvider 看起来像这样时,容器能够通过构造函数注入所需的依赖项(IHandlerIPreProcessor):

public class MailProvider : IProvider
{
    private readonly IHandler handler;
    private readonly IPreProcessor preProcessor;

    public MailProvider(IHandler handler, 
        IPreProcessor preProcessor)
    {
        this.handler = handler;
        this.preProcessor = preProcessor;
    }

    public void SomeAction() { ... }
}

【讨论】:

  • +1。你是绝对正确的。最简单的方法是使用ifswitch 语句来获得正确的实现。在更复杂的场景中,您可以使用Reflection 来生成适当的实例。 XML 文件甚至可能包含必须动态加载的程序集的名称。最后,可以使用 IOC 容器来完成所有这些工作。
  • @OlivierJacot-Descombes:我正在编写一个使用 DI 容器的示例,但为了让答案尽可能简单,我决定将其省略。
  • @OlivierJacot-Descombes 和 Steven,感谢您的回答。唯一的问题是当我有很多(在我的情况下是几十个......)处理程序和预处理器时,if / switch 语句将是无穷无尽的。
  • @Steven,感谢您的回答。唯一的问题是当我有很多(在我的情况下是几十个......)处理程序和预处理器时,if / switch 语句将是无止境的
  • @Steven,再次感谢。我能否改用 AutoFac(或任何其他 DI/IOC 收费)来解析 IProvider(MailProvider、FTPProvider 等)并将其功能(处理程序和预处理)作为属性注入在 IProvider 接口中?它看起来像是明显需要的解决方案。如果是这样,您是否知道如何做到这一点的良好参考/示例?
【解决方案2】:

我将创建一个实现这些接口并注入功能的包装类。

示例接口

public interface IProvider
{
    string ProvideSomething(int id);
}

public interface IPreProcessor
{
    void PreProcess(string parameter);
}

public interface IHandler
{
    void HandleSomething();
}

包装器将实现所有这些接口

public class ProviderWrapper : IProvider, IPreProcessor, IHandler
{
    private IProvider _provider;
    private IPreProcessor _preProcessor;
    private IHandler _handler;

    public ProviderWrapper(IProvider provider, IPreProcessor preProcessor, IHandler handler)
    {
        _provider = provider;
        _preProcessor = preProcessor;
        _handler = handler;
    }

    #region IProvider Members

    public string ProvideSomething(int id)
    {
        return _provider.ProvideSomething(id);
    }

    #endregion

    #region IPreProcessor Members

    public void PreProcess(string parameter)
    {
        _preProcessor.PreProcess(parameter);
    }

    #endregion

    #region IHandler Members

    public void HandleSomething()
    {
        _handler.HandleSomething();
    }

    #endregion
}

现在,您可以根据配置文件实例化具有所需功能的ProviderWrapper,并结合不同的接口实现。

var provider = new ProviderWrapper(providerX, preProcessorY, handlerZ);

【讨论】:

    【解决方案3】:

    也许您可以像通常在运行时一样创建所有这些类的实例,然后将它们序列化为 xml。然后当你想加载你的“配置”时,你只需要反序列化。

    请参阅here 进行序列化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-28
      • 2013-06-27
      • 1970-01-01
      • 2013-02-24
      • 2021-10-20
      • 2018-12-19
      • 2014-09-15
      相关资源
      最近更新 更多