UnityContainer和内部构造函数

本文关键字:构造函数 内部 UnityContainer | 更新日期: 2023-09-27 17:59:58

我有一个带有内部构造函数的类,想从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<MyClass>())来创建。

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

UnityContainer和内部构造函数

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

首先,Unity似乎通过内部解析IConstructorSelectorPolicy来选择要使用的构造函数。Unity中包括public abstract class ConstructorSelectorPolicyBase<TInjectionConstructorMarkerAttribute>,其中包括这个宝石:

/// <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使用你自己的构造函数选择器策略,它可以选择任何构造函数,你就是黄金。

关于如何制作和使用自己的容器扩展,有很好的文档,因此,我认为可以制作自己的CustomConstructorSelectorPolicy,其中包括DefaultUnityConstructorSelectorPolicy(它从抽象基类派生,除非您注册其他内容,否则是默认的)和ConstructorSelectorPolicyBase(直接从中派生可能不太好,因为关键方法不是virtual,但您可以重用代码)的相关部分。

因此,我认为这是可行的,只需要适度的麻烦,但从工程的角度来看,最终结果将是相当"纯粹"的。

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>());
})); 

尽管您不必公开构造函数,但它是混淆代码的好方法:-)

只需将类内部和构造函数公开即可。。。

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

可能有一些变通方法/技巧可以让你用Unity 9做到这一点(我不知道是否有),但一般来说,如果你想让一个类由Unity(或任何IOC容器)管理,它需要通过公共构造函数来公开。

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