web-dev-qa-db-ja.com

Unityを使用して複数のコンストラクターでインスタンスを解決する

Unityを使用してクラスのインスタンスを作成したいと思います。このクラスには、同じ数のパラメーターを持つ2つのコンストラクターがあります。

インスタンス化は次のとおりです。

_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));

そして、ここにコンストラクターがあります:

    public GradeType(string gradeTypeStringFromXmlFile)
    {
        _gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
    }

    public GradeType(Enum.GradeType gradeType)
    {
        _gradeType = gradeType;
    }

これを実行しようとすると、例外が発生しますGradeType型には長さ1のコンストラクターが複数あります

1つのコンストラクターに属性[InjectionConstructor]を設定して、1つのコンストラクターで機能させることはできますが、他のコンストラクターを使用してUnityでインスタンスを作成することはできません。

同じ数のパラメーターを持つ複数のコンストラクターを持ち、それでもUnityを使用してインスタンスを作成する方法はありますか?

19
FatAlbert

はい、Unityにどのコンストラクターを使用するかを指示することは可能ですが、これは、タイプを InjectionConstructor に登録する場合にのみ実行できます。両方のコンストラクターを使用する場合は、登録に名前を付け、解決時にその名前を使用する必要があるため、さらに複雑になります。

Unityバージョン2.1.505でビルドされたサンプル:

var continer = new UnityContainer();

continer.RegisterType<IGradeType, GradeType>("stringConstructor", 
    new InjectionConstructor(typeof(string)));

continer.RegisterType<IGradeType, GradeType>("enumConstructor",
    new InjectionConstructor(typeof(EnumGradeType)));

IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" , 
    new DependencyOverride(typeof(string), "some string"));

IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor", 
    new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
28
nemesv

リフレクションを使用し、 戦略パターン に従う代替オプション。

1)コンストラクターの引数の基本クラスを作成します

public abstract class ConstructorArgs
{
}

2)さまざまな具体的な引数クラスのシーケンスを作成します。

public class StringArg : ConstructorArgs
{
    public string _gradeTypeStringFromXmlFile { get; set; }

    public StringArg (string gradeTypeStringFromXmlFile)
    {
        this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
    }
}

public class EnumArg : ConstructorArgs
{
    public Enum.GradeType _gradeType { get; set; }

    public EnumArg (Enum.GradeType gradeType)
    {
        this._gradeType = gradeType ;
    }
}

3)GradeTypeクラスで、Reflectionに必要なメソッドを作成します。 ParseArgumentsは、引数をスキャンしてプロパティを探し、見つかった引数ごとに、SetPropertyを使用してその値をGradeTypeのそれぞれのプロパティにコピーします。マッチングにはプロパティ名を使用するため、GradeTypeと具象ConstructorArgsの両方で同じプロパティ名を維持することが重要です。

        private void SetProperty(String propertyName, object value)
        {
            var property = this.GetType().GetProperty(propertyName);
            if (property != null)
                property.SetValue(this, value);
        }
        private void ParseArguments(ConstructorArgs args)
        {
            var properties = args.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                this.SetProperty(propertyInfo.Name, 
                   args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
            }
        }

4)GradeTypeクラスで、それぞれのプロパティを作成します(具体的なConstructorArgsで使用したのとまったく同じ名前とタイプを使用する必要がありますが、任意のアクセス修飾子を使用できます)

    public string _gradeTypeStringFromXmlFile { get; set; }
    public Enum.GradeType _gradeType { get; set; }

5)タイプConstructorArgsのパラメーターを使用してGradeTypeクラスのコンストラクターを作成します。

    public GradeType(ConstructorArgs args)
    {
        this.ParseArguments(args);
    }

6)単一のコンストラクターを使用してUnityにGradeTypeを登録できるようになりましたが、解決時に引数としてさまざまな型を渡すことができます。

    _unityContainer.RegisterType<IGradeType, GradeType>(
       new InjectionConstructor( typeof(ConstructorArgs) ));

    var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
    IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args1)});

    var args2 = new EnumArg(gradeType); // enum
    IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args2)});

Reflectionにはパフォーマンスの低下が伴うため、反復でタイプを繰り返し解決することを計画している場合、そのアプローチは理想的ではない可能性があります。

1
celetron