UnityContainer 和内部构造函数

发布于 2024-11-16 09:51:55 字数 529 浏览 5 评论 0原文

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

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

I have a class with internal constructor and want to Resolve it from Unity (2.0).

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

then I'm doing

_container.Resolve<MyClass>();

when I do so I have an exception

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

IService is registered and the only problem is that constructor is internal.
I really want this class to be public, but I want it to be creatable only via a factory (in which I'm actually calling container.Resolve<MyClass>()).

Is there a way to make Unity see that internal constructor? Like InternalsVisibleTo or something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

孤凫 2024-11-23 09:51:55

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

首先,Unity 似乎通过内部解析 IConstructorSelectorPolicy 来选择要使用的构造函数。 Unity 中包含公共抽象类 ConstructorSelectorPolicyBase,其中包含以下 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 是此类中的私有静态 方法,最终最终调用 Type.GetConstructors (不带参数的重载,仅返回 public 构造函数)。这告诉我,如果您可以安排 Unity 使用您自己的构造函数选择器策略,该策略将能够选择任何构造函数,那么您就是黄金。

很好的文档介绍如何制作并利用您自己的容器扩展,因此我认为很有可能制作您自己的 CustomConstructorSelectorPolicy ,其中包含 DefaultUnityConstructorSelectorPolicy (派生来自抽象基类,并且是默认值,除非您注册其他内容)和 ConstructorSelectorPolicyBase< /a> (直接从中派生可能效果不佳,因为关键方法不是虚拟,但您可以重用该代码)。

因此我想说这是可行的,有一定的麻烦,但从工程的角度来看,最终结果将是相当“纯粹的”。

I dug a little into how you might extend Unity for this purpose, and found some interesting information.

First, it seems that Unity selects which constructor to use by internally resolving an IConstructorSelectorPolicy. Included in Unity is the public abstract class ConstructorSelectorPolicyBase<TInjectionConstructorMarkerAttribute>, which includes this 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 and company are private static methods in this class which ultimately end up calling Type.GetConstructors (the overload with no parameters, which only returns public constructors). This tells me that if you can arrange for Unity to use your own constructor selector policy, which would be able to select any constructor, you are golden.

There is good documentation about how to make and utilize your own container extensions, so I imagine it's quite possible to make your own CustomConstructorSelectorPolicy that includes the relevant portions of DefaultUnityConstructorSelectorPolicy (which derives from the abstract base class and is the default unless you register something else) and ConstructorSelectorPolicyBase (deriving from this directly would probably not work well because key methods are not virtual, but you can reuse the code).

Therefore I 'd say it's doable with a moderate amount of hassle, but the end result would be quite "pure" from an engineering point of view.

沫雨熙 2024-11-23 09:51:55

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

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

Unity will only look at public constructors, so you need to make this constructor public.

I really want this class to be public,
but I want it to be creatable only via
a factory

In that case, create a factory:

public class MyClassFactory : IMyClassFactory
{
    private readonly IService service;

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

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

And register:

_container.Register<IMyClassFactory, MyClassFactory>();

And resolve:

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

You can also use Unity's InjectionFactory:

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

For this to work the assembly that holds this code should be able to see the internals of the assembly that holds the MyClass. In other words the MyClass assembly should be marked with InternalsVisibleTo.

What would also work is the following:

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

Although you won't have to make the constructor public, it is a great way to obfuscate your code :-)

你是我的挚爱i 2024-11-23 09:51:55

只需将类内部和构造函数设为公共...

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

Just make the class internal and the constructor public...

  1. Interface public
  2. Class internal
  3. Constructor of class public.
看透却不说透 2024-11-23 09:51:55

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

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

It's possible there are workarounds/hacks that would allow you to do this with Unity 9I don't know if any), but in general if you want a class to be managed by Unity (or any IOC container), it needs to be public with a public constructor.

One option might be to make an abstract factory that creates the class that has a public constructor, and keep the class's constructor internal. The downside is then your factory will be managed by Unity, but your class itself will not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文