12

内部コンストラクターを持つクラスがあり、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か何かのように?

4

4 に答える 4

10

この目的のために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の関連部分を含む独自の拡張機能を作成することはかなり可能だと思います。(キーメソッドはそうではないため、これから直接派生することはおそらくうまく機能しませんが、コードを再利用できます)。DefaultUnityConstructorSelectorPolicyConstructorSelectorPolicyBasevirtual

したがって、適度な手間で実行できると思いますが、最終的な結果は、エンジニアリングの観点からはかなり「純粋」になります。

于 2011-06-20T14:30:11.017 に答える
7

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

コンストラクターを公開する必要はありませんが、コードを難読化するのに最適な方法です:-)

于 2011-06-20T14:17:51.763 に答える
3

クラスを内部にし、コンストラクターをパブリックにするだけです...

  1. インターフェースパブリック
  2. クラス内部
  3. クラスpublicのコンストラクタ。
于 2011-11-29T16:12:17.717 に答える
0

Unity 9でこれを行うことができる回避策/ハックがある可能性があります(あるかどうかはわかりません)が、一般に、クラスをUnity(または任意のIOCコンテナー)で管理する場合は、パブリックにする必要がありますパブリックコンストラクターを使用します。

1つのオプションは、パブリックコンストラクターを持つクラスを作成する抽象ファクトリを作成し、クラスのコンストラクターを内部に保持することです。欠点は、ファクトリがUnityによって管理されることですが、クラス自体は管理されません。

于 2011-06-20T14:15:31.160 に答える