c# - 为某些 caSTLe 组件的运行时设置而苦苦挣扎

标签 c# .net inversion-of-control castle-windsor

<分区>

我的问题(我觉得)可以简单的用这个问题来描述:

我怎样才能让“管理器”类拥有一个特定接口(interface)的所有可能具体实现的列表,这些接口(interface)已注册为组件注入(inject)其中,以便它可以遍历它们? (由于烦人的架构原因,我无法使用 app.config 来注册我的组件 - 所以我猜只能进行运行时注册)

编辑:
啊哈! - 发现我可以使用类似以下内容传递我自己的配置文件:

var container = new WindsorContainer(new XmlInterpreter("filename"));

所以 - 我可以只使用我在下面声明的 xml 配置路由 - 这意味着我不必担心子对象的实例化,因为它们在我的现实生活中已经自行注入(inject)了依赖项。

编辑:




不过,为了尝试帮助您理解我的问题,我在下面列出了一个简单的重现来显示我的困惑! (请原谅糟糕的域概念!)

这是一个简单的控制台应用程序,它有一个管理器,该管理器将对给定的对象列表运行操作。

代码:

public class Program {
    public static void Main() {
        var container = new WindsorContainer(new XmlInterpreter());
        var manager = container.Resolve<AnimalManager>();
        manager.PokeAnimals();
        container.Release(manager);
    }
}

public interface IMakeNoise {
    void MakeNoise();
}

public class Cat : IMakeNoise {
    public void MakeNoise() {
        Console.WriteLine("Meow");
    }
}

public class Dog : IMakeNoise {
    public void MakeNoise() {
        Console.WriteLine("Woof");
    }
}

public class AnimalManager {
    private readonly IList<IMakeNoise> noisemakers = new List<IMakeNoise>();

    public AnimalManager(IList<IMakeNoise> noisemakers) {
        this.noisemakers = noisemakers;
    }

    public void PokeAnimals() {
        foreach (var noisemaker in noisemakers) {
            noisemaker.MakeNoise();
        }
    }
}

使用的配置文件如下所示:

<configSections>
  <section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>
</configSections>

<castle>
  <components>
    <component id="Dog" service="CastleListTest.IMakeNoise, CastleListTest"
          type="CastleListTest.Dog, CastleListTest" />

    <component id="Cat" service="CastleListTest.IMakeNoise, CastleListTest"
          type="CastleListTest.Cat, CastleListTest" />

    <component id="AnimalManager" service="CastleListTest.AnimalManager, CastleListTest"
      type="CastleListTest.AnimalManager, CastleListTest">
      <parameters>
        <noisemakers>
          <list>
            <item>${Cat}</item>
            <item>${Dog}</item>
          </list>
        </noisemakers>
      </parameters>
    </component>
  </components>
</castle>

我的问题:

以上一切正常。但是我无法在 xml 配置文件中定义我拥有哪种噪声发生器。直到运行时我才知道我是否有一只猫、一只狗、两只猫和一条金鱼或其他什么...

下面的代码最好地描述了我能看到的执行此操作的唯一方法。这个问题是我必须实例化我的 NoiseMakers:“狗”和“猫”。这似乎是错误的,反对 CaSTLe,在我的实际例子中,这是我真的不想做的事情。那我做错了什么? 我怎样才能让“管理器”类拥有一个特定接口(interface)的所有可能具体实现的列表,这些接口(interface)已注册为注入(inject)其中的组件,以便它可以遍历它们?

        var container = new WindsorContainer();

        container.AddComponent("Dog", typeof(IMakeNoise), typeof(Dog));
        container.AddComponent("Cat", typeof(IMakeNoise), typeof(Cat));
        container.AddComponent(
            "AnimalManager", typeof(AnimalManager), typeof(AnimalManager)
            );

        IList<IMakeNoise> noisemakers = new List<IMakeNoise> {new Dog(), new Cat()};

        var manager = container.Resolve<AnimalManager>(
            new Hashtable { { "noisemakers", noisemakers } }
            );

        manager.PokeAnimals();
        container.Release(manager);

最佳答案

最简单的事情是实现一个 SubDependancyResolver 来解析数组,然后在构造函数中传入类型。

取自hammett blog

public class ArrayResolver : ISubDependencyResolver
{
    private readonly IKernel kernel;

    public ArrayResolver(IKernel kernel)
    {
          this.kernel = kernel;
    }

    public object Resolve(CreationContext context, ISubDependencyResolver parentResolver,
                          ComponentModel model,
                          DependencyModel dependency)
    {
         return kernel.ResolveAll(dependency.TargetType.GetElementType(), null);
    }

    public bool CanResolve(CreationContext context, ISubDependencyResolver parentResolver,
                          ComponentModel model,
                          DependencyModel dependency)
    {
          return dependency.TargetType != null &&
        dependency.TargetType.IsArray &&
        dependency.TargetType.GetElementType().IsInterface;
    }
}

public class AnimalManager
{
     private readonly IAnimal[] _animals;
     public AnimalManager(IAnimal[] animals)
     {
         _animals = animals
     }
}    

//Setup container somewhere
public IWindsorContainer ConfigureContainer()
{
     IWindsorContainer container = new WindsorContainer().Register(AllTypes.FromAssemblyContaining<Cat>());
     container.Kernel.Resolver.AddSubResolver(typeof(ArrayResolver));

     IAnimalManager manager = container.Resolve<AnimalManager>();
}

容器现在会将所有动物类型注入(inject)到 AnimalManager 类中供您使用。

科林·G

关于c# - 为某些 caSTLe 组件的运行时设置而苦苦挣扎,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1311120/

相关文章:

c# - 转换和界面困境

c# - 有什么方法可以检查 oauth token 是否过期?

c# - 无法解析从外部 dll 加载的 Controller

.net - 国际奥委会最佳实践: How to best manage dependency graph?

C# 优雅地关闭没有 UI 的单独应用程序

c# - 为什么这个未使用的变量没有给出警告?

c# - Worker Role流程-配置值轮询

.net - .NET 中非矩形形式的平滑边缘

.net - NHibernate 的 SimpleDB 方言

java - Spring 中的 AutoWired Setters 而不是 AutoWired 实例变量