【问题标题】:UnityContainer and internal constructorUnityContainer 和内部构造函数
【发布时间】:2011-09-18 17:25:50
【问题描述】:

我有一个带有内部构造函数的类,想从 Unity (2.0) 中解决它。

public class MyClass {
    internal MyClass(IService service) {
    }
}

那我在做

_container.Resolve<MyClass>();

当我这样做时,我有一个例外

Exception is: InvalidOperationException - The type MyClass cannot be constructed. 

IService 已注册,唯一的问题是构造函数是内部的。 我真的希望这个类是公开的,但我希望它只能通过工厂创建(我实际上在其中调用 container.Resolve&lt;MyClass&gt;())。

有没有办法让 Unity 看到内部构造函数?像 InternalsVisibleTo 什么的?

【问题讨论】:

  • 你为什么不想公开这个ctor?你在创建一个可重用的库吗?

标签: c# unity-container ioc-container internalsvisibleto


【解决方案1】:

可能有一些变通方法/黑客可以让您使用 Unity 9 执行此操作我不知道是否有),但一般来说,如果您希望一个类由 Unity(或任何 IOC 容器)管理,它需要使用公共构造函数公开。

一种选择可能是创建一个抽象工厂来创建具有公共构造函数的类,并将类的构造函数保持在内部。缺点是你的工厂将由 Unity 管理,但你的类本身不会。

【讨论】:

  • “它需要通过公共构造函数公开。” - 这是错误的,Unity 与具有公共构造函数的内部类完美配合。当我想用 IoC 管理我的内部依赖时就是这种情况。我所做的是一个抽象工厂。在其中我想创建一个 MyClass 的实例。我可以做 new MyClass(_container.Resolve()),但不想手动做。
【解决方案2】:

Unity 只会查看公共构造函数,因此您需要将此构造函数设为公开。

我真的希望这门课是公开的, 但我希望它只能通过 工厂

在这种情况下,创建一个工厂:

public class MyClassFactory : IMyClassFactory
{
    private readonly IService service;

    public MyClassFactory(IService service)
    {
        this.service = service;
    }

    MyClass IMyClassFactory.CreateNew()
    {
        return new MyClass(this.service);
    }
}

并注册:

_container.Register<IMyClassFactory, MyClassFactory>();

并解决:

_container.Resolve<IMyClassFactory>().CreateNew();

你也可以使用Unity的InjectionFactory:

container.Register<MyClass>(new InjectionFactory(c => 
{   
    return new MyClass(c.Resolve<IService>());
})); 

为此,包含此代码的程序集应该能够看到包含MyClass 的程序集的内部。换句话说,MyClass 程序集应该用InternalsVisibleTo 标记。

以下内容也可以:

public static class MyClassFactory
{
    public static MyClass CreateNew(IService service)
    {
        return new MyClass(service);
    }
}

container.Register<MyClass>(new InjectionFactory(c => 
{   
    return MyClassFactory.Create(c.Resolve<IService>());
})); 

虽然您不必公开构造函数,但这是混淆代码的好方法 :-)

【讨论】:

  • 谢谢,这几乎正是我正在做的 :) 我只是不想在每次 MyClass 依赖项发生变化时都更改该工厂。因此,以某种方式直接通过 Unity 寻找一种方法
【解决方案3】:

我深入研究了如何为此目的扩展 Unity,并发现了一些有趣的信息。

首先,Unity 似乎通过内部解析IConstructorSelectorPolicy 来选择要使用的构造函数。 Unity 中包含 public abstract class ConstructorSelectorPolicyBase&lt;TInjectionConstructorMarkerAttribute&gt;,其中包含此 gem:

/// <summary>
/// Choose the constructor to call for the given type.
/// </summary>
/// <param name="context">Current build context</param>
/// <param name="resolverPolicyDestination">The <see cref='IPolicyList'/> to add any
/// generated resolver objects into.</param>
/// <returns>The chosen constructor.</returns>
public SelectedConstructor SelectConstructor(IBuilderContext context, IPolicyList resolverPolicyDestination)
{
    Type typeToConstruct = context.BuildKey.Type;
    ConstructorInfo ctor = FindInjectionConstructor(typeToConstruct) ?? FindLongestConstructor(typeToConstruct);
    if (ctor != null)
    {
        return CreateSelectedConstructor(context, resolverPolicyDestination, ctor);
    }
    return null;
}

FindInjectionConstructor 和 company 是此类中的 private static 方法,最终调用 Type.GetConstructors (没有参数的重载,仅返回 public 构造函数)。这告诉我,如果您可以安排 Unity 使用您自己的构造函数选择器策略,它能够选择任何构造函数,那么您就是黄金。

good documentation 介绍了如何制作和使用您自己的容器扩展,所以我想很有可能制作您自己的 CustomConstructorSelectorPolicy,其中包括 DefaultUnityConstructorSelectorPolicy 的相关部分(派生自抽象基类和是默认值,除非你注册了其他东西)和ConstructorSelectorPolicyBase(直接从这里派生可能效果不佳,因为关键方法不是virtual,但你可以重用代码)。

因此,我会说它是可行的,但从工程的角度来看,最终结果将是相当“纯粹”的。

【讨论】:

【解决方案4】:

只需将类设为内部类并将构造函数设为公开...

  1. 接口公开
  2. 类内部
  3. public 类的构造函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多