最初の部分では、トピックについて説明しました。
- 設置
- 登録(基本、プロファイル、プラグイン、スキャン、展開)
このパートでは、以下について説明します。
- コンストラクター(単純型、デフォルトのコンストラクター、複合型、強制、引数の設定)
- プロパティ(単純なプロパティ設定、ビルトインプロパティ設定、フレームワークを使用したプロパティの設定、既存のクラスの拡張)
- ライフタイム
コンストラクター
IoCコンテナーの依存関係の解決に関連する実際のプログラミングでの非常に重要な質問、いくつかのコンストラクターを持つクラスについてはどうですか。 それらを初期化する方法、パラメータを設定する方法、変更する方法など。 以下の質問のほとんどが回答されることを望みます。 StructureMapは本当に強力で柔軟です。
フレームワークの機能を説明する前に、テストクラスについて説明する必要があります。 今回はより困難になります。 継承、単純型と複合型のコンストラクタ。
そこで、次のクラスを用意しましょう。
public interface IClassA : IClass { int A { get; set; } } public interface IClassB : IClass {} public class ClassA : IClassA { public int A { get; set; } public int B { get; set; } public Class1 Class1 { get; set; } [DefaultConstructor] public ClassA() {} public ClassA(int a) { A = a; } } public class ClassB : IClassB { public IClassA ClassA; public ClassB(IClassA classA) { ClassA = classA; } } public class ClassM : IClassA { public int A { get; set; } public ClassM(int a) { A = a; } public ClassM(int a, int b) { A = a + b; } }
それらは現時点でいくつかの冗長性を備えており、ストーリーでもう少し役立ちます。
それでは、最も単純なオプションから始めましょう。
単純型
まず、コンストラクターの1つが整数パラメーターを受け入れるClassAクラスから始めましょう。
public class WithSimpeArguments { public IContainer Container; public WithSimpeArguments() { Container = new Container(x => x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) ); } }
例からわかるように、値を使用してコンストラクターを初期化する指示が、すでによく知られている宣言に追加されます。 この場合、int型のパラメーターは1つしかないため、コンストラクター宣言は単純化されたスキームに従います。 初期化プロセスで、すぐに値を示しました。 同時に、StructureMapは値が整数でなければならないことを通知します。
操作を確認するには、すでにおなじみのコードを呼び出すことができます
private static string WithSimpleArgumentsExample() { var container = new WithSimpeArguments().Container; var classA = (ClassA) container.GetInstance<IClassA>(); return classA.A.ToString(); }
その結果、数字の5がコンソールに表示されます。
コンストラクターに同じ型の2つのパラメーターがある場合の例は複雑です。 実験クラスClassMとして。
public class WithMultipleSimpeArguments { public IContainer Container; public WithMultipleSimpeArguments() { Container = new Container(x => x.For<IClassA>().Use<ClassM>() .Ctor<int>("a").Is(6) .Ctor<int>("b").Is(5)); } }
ここで、 .Ctor <int>( "a")。Is(6)に引数の名前が追加されているため、フレームワークは引数の値を正確に一致させることができます。 フレームワークは常に最も「貪欲な」コンストラクタを見つけ、すべての引数を初期化することを望みます。 クラスの現在の実装では、2番目の引数の値の設定を省略することはできません。 ただし、デフォルトで使用するコンストラクターをStructureMapに指示できます。そのためには、 DefaultConstructor属性を使用する必要があります。
デフォルトのコンストラクター
DefaultConstructor属性を使用すると、クラスのインスタンス化に使用するコンストラクターを明示的に指定できます。 そのため、前の例では変数bの宣言を省略でき、プロセスに何も落ちませんでした。
public class ClassM : IClassA { public int A { get; set; } [DefaultConstructor] public ClassM(int a) { A = a; } public ClassM(int a, int b) { A = a + b; } }
これで、1つのパラメーターでコンストラクターを使用できます
複合タイプ
構造型自体は、複合型によるすべての依存関係を検出および解決するため、複合型の操作は非常に簡単です。 つまり 最初にクラスを見ると、ClassBクラスがClassAクラスによって初期化されていることがわかります。 この種類の依存関係を解決するために特別なものは必要ないことがわかりました。
public class WithObjectArguments { public IContainer Container; public WithObjectArguments() { Container = new Container(x => { x.For<IClassA>().Use<ClassA>().Ctor<int>().Is(5); x.For<IClassB>().Use<ClassB>(); }); } }
例からわかるように、追加の演算子は適用されません。 ただし、ClassAによって初期化されるClassBクラスを要求できます。
private static string WithObjectArgumentsExample() { var container = new WithObjectArguments().Container; var classA = (ClassB) container.GetInstance<IClassB>(); return classA.ClassA.A.ToString(); }
数字の5が画面に表示されます。
タイプキャスト
ClassBクラスの宣言を見ると、コンストラクターの変数が特定の型ではなくインターフェイスであることがわかります。 インターフェースの代わりに、コンストラクターがClassAクラスを受け入れるようにクラスを書き直します
public class ClassB : IClassB { public ClassB(ClassA classA) { ClassA = classA; } }
コンテナが単一のデータ型を登録して返すため、型バインディングは発生しません。 私たちの場合、それはIClassAです。
private static string WithObjectArgumentsForwardingAndWiringExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container.GetInstance<IClassB>(); return classB.ClassA.A.ToString(); }
ClassAクラスのデフォルトコンストラクターが呼び出されるため、バインディングは発生しなかったため、このコードは0を返します。
あなたはそれが問題ではないと推測しました。 StructureMapが何につながるかを指定できます。 これは、ソースタイプを指定する必要のあるForwardコマンドを使用して行われます。
public class WithObjectArgumentsForwarding { public IContainer Container; public WithObjectArgumentsForwarding() { Container = new Container(x => { x.For<IClassA>().Use<ClassA>().Ctor<int>().Is(5); x.Forward<IClassA, ClassA>(); x.For<IClassB>().Use<ClassB>(); }); } }
これで、WithObjectArgumentsForwardingAndWiringExampleメソッドを呼び出して、応答で5を取得できます。
引数の定義
ほとんどの場合、事前にクラスの引数を見つけて設定することは不可能であり、必要なクラスを作成した時点でのみ引数が認識されます。 そして、これらはプログラムを書く作業日であるため、クラスの作成時に引数を設定するサポートは、StructureMapに表示されますが、助けにはなりません。
新しい引数値を使用してクラスを呼び出すプロセスは、Yodaマスターの言語での通信に似ていますが、これはDSLの特異性です。 技術的な制限です。
したがって、新しいClassAクラスでClassBクラスを呼び出してみましょう。 これを行うには、Withステートメントが必要です。
private static string WithObjectArgumentsOverridingExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container .With(new ClassA(8)) .GetInstance<IClassB>(); return classB.ClassA.A.ToString(); }
この場合、フレームワークはクラスインスタンスをどの引数にマップするかを明確に決定できるため、引数の名前を指定する必要はありません。
クラスに対してより多くの引数を指定する必要がある場合は、パラメーター名とその値でより多くのWithメソッドを使用する必要があります。
private static string WithObjectArgumentsOverridingExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container .With(new ClassA(8)) .With("s").EqualTo(5) .GetInstance<IClassB>(); return classB.ClassA.A.ToString(); }
これで、デザイナーとの作業は、アプリケーションにあるほぼすべてのアーキテクチャアプローチを解決するのに十分にカバーされたと思います。
プロパティ
必要なコンストラクターパラメーターに加えて、クラスインスタンスを作成するときにクラスプロパティの値を設定すると非常に便利です。 つまり 書く代わりに
var classA = new ClassA(); classA.A = 8; classA.B = 20;
次のように、プロパティをより短く、よりきれいに設定できます。
var classA = new ClassA { A = 8, B = 20 };
StuctureMapの場合、それは非常に可能であり、同じエレガントで理解しやすい方法です。
シンプルなプロパティ設定
上の図のように、最も単純なケースでは、 SetPropertyメソッドを使用してパラメーターを設定できます 。
public class WithArgumentsSetProperty { public IContainer Container; public WithArgumentsSetProperty() { Container = new Container(x => x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .SetProperty(p => { pA = 8; pB = 20; })); } }
例からわかるように、プロパティは強く型付けされており、intelliSenseヒントを使用できます。 プロパティの設定は簡単で簡単です。 すべてのプロパティを設定できるわけではなく、クラスのインスタンスを構築する段階で初期化するプロパティのみを設定できることは明らかです。
組み込みプロパティ定義
インラインパラメータ設定を使用するには、 Setterメソッドが使用されます。 この方法を使用すると、一度に1つのパラメーターの値を設定できます。 メソッドの引数は関数であるため。
最も簡単なことは、初期化のパラメーターを明示的に設定することです。
public class WithArgumentsSetterExplicit { public IContainer Container; public WithArgumentsSetterExplicit() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter(c => c.Class1).Is(new Class1()); }); } }
この例は、プロパティClass1が常に新しいクラスClass1によって初期化されることを示しています。 このようなレコードは、クラスに同じタイプのプロパティが複数ある場合に適用する必要があります。 特定のタイプのプロパティが1つしかない場合、フレームワークは、渡されたパラメーターの値を割り当てるプロパティを個別に決定できます。
したがって、暗黙的なプロパティの初期化。
public class WithArgumentsSetterImplicit { public IContainer Container; public WithArgumentsSetterImplicit() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter<Class1>().Is(new Class1()); }); } }
この例では、プロパティの名前を指定しませんでしたが、Class1型のプロパティは1つしかないため、すべてうまくいきました。
フレームワークでプロパティを設定する
StructureMapは自動的にクラスインスタンスをコンストラクター引数に置き換えることができるため、クラスプロパティを自動的に設定できますか?
もちろんできます!
しかし、もちろん彼はこれを自由に行うのではなく、すべてのフィールドに対してではなく、オートコンプリートとして示されるフィールドに対してのみ行います。
前の例を変更して、プロパティの依存関係が構造マップによって解決および制御されるようにすることができます。
public class WithArgumentsSetterImplicitDefault { public IContainer Container; public WithArgumentsSetterImplicitDefault() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter<Class1>().IsTheDefault(); }); } }
この例では、新しいIsTheDefaultメソッドが登場しました 。これは、独自の手段で依存関係を解決するようフレームワークに指示します。 つまり この場合、クラスClassAのタイプClass1のプロパティが作成され、Class1の登録方法に基づいて割り当てられます。
特定のタイプのすべてのプロパティをデフォルト値で初期化する必要があると言える場合、バッチパラメータの初期化もあります。 これを行うには、 SetAllPropertiesコマンドを使用します。
public class WithArgumentsSetterBatchImplicitDefault { public IContainer Container; public WithArgumentsSetterBatchImplicitDefault() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5); x.SetAllProperties(c => c.OfType<Class1>()); }); } }
このヒントを使用して、StructureMapは、プロパティタイプがClass1である呼び出し先クラスのすべてのプロパティを自動的に初期化します。
既存のクラスのアップグレード
時として、作成に影響を与えることができない既成のクラスしか取得できないことがあります。 同時に、自動モードでクラスフィールドのヒープの充填を自動化したいです。 そして、これはStructureMapで可能です。
私たちのシステムに作成されていないClassAを私たちのところにやってみましょう。 Class1型のプロパティを初期化する必要があります。 最初にStructureMapをセットアップします。
public class WithArgumentsBuildUp { public IContainer Container; public WithArgumentsBuildUp() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.Forward<IClass1, Class1>(); x.SetAllProperties(c => c.OfType<Class1>()); }); } }
これで、利用可能な構成に従ってオブジェクトを完成させるBuildUpメソッドを呼び出すことができます。
var container = new WithArgumentsBuildUp().Container; var classA = new ClassA(14); container.BuildUp(classA);
2行目のプロパティClass1 = nullでは、BuildUpを呼び出した後、オブジェクトは完全に準備ができています。
ライフタイム
重要な要因は、オブジェクトの寿命を制御する能力です。 いくつかのクラスでは、同じインスタンスを取得する必要があり、他のクラスでは、新しいインスタンスを取得する必要があります。 これは、コンテナ内のルールの作成中に制御することもできます。
このフレームワークは、7つのオブジェクトライフタイム管理ポリシーで動作します。
- リクエストごと(デフォルト)-毎回新しいオブジェクトが作成されます。
- HttpContextLifecycle
- シングルトンライフサイクル
- ThreadlocalStorageLifecycle
- UniquePerRequestLifecycle-要求されたオブジェクトを初期化するオブジェクトのチェーン全体が一意であることを保証します。
- HttpSessionLifecycle
- HybridLifecycleはHttpSessionLifecycleまたはThreadlocalStorageLifecycleです
- HybridSessionLifecycle-またはHttpContextLifecycle、またはHttpSessionLifecycle
例として単純なクラスを使用して、それらのいくつかを考えてみましょう。
public class ClassX : IClassX { public int Counter { get; private set; } public void Increase() { Counter++; } } public interface IClassX {}
Singletonを最初の行とします。
public class LifecycleSingleton { public IContainer Container; public LifecycleSingleton() { Container = new Container(x => x.For<IClassX>().LifecycleIs(new SingletonLifecycle()).Use<ClassX>()); Container = new Container(x => x.For<IClassX>().Singleton().Use<ClassX>()); } }
簡易的な方法は、基本的な生活政策のために定義されています。 つまり Singleton()とLifecycleIs(新しいSingletonLifecycle())の両方を使用できます。
チェックとして、視覚的な例を使用できます。
private static string LifecycleSingleton() { var singleton = new LifecycleSingleton().Container; var classX = (ClassX) singleton.GetInstance<IClassX>(); classX.Increase(); Console.WriteLine(classX.Counter); classX = (ClassX) singleton.GetInstance<IClassX>(); classX.Increase(); Console.WriteLine(classX.Counter); return "done"; }
その結果、データがコンソールに表示されます:「1、2、完了」。 簡単な発表で、「1、1、Done」と表示されます。
クラスのインスタンスを単一のスレッド内に保存するには、 ThreadLocalStorageLifecycle 、または短い形式のHybridHttpOrThreadLocalScopedを使用します
public class LifecycleThreadLocal { public IContainer Container; public LifecycleThreadLocal() { Container = new Container(x => x.For<IClassX>() .LifecycleIs(new ThreadLocalStorageLifecycle()) .Use<ClassX>()); Container = new Container(x => x.For<IClassX>() .HybridHttpOrThreadLocalScoped() .Use<ClassX>()); } }
HttpContextLifecycleに定義されているHttpContextScopedの略語
public class LifecycleHttpContext { public IContainer Container; public LifecycleHttpContext() { Container = new Container(x => x.For<IClassX>() .LifecycleIs(new HttpContextLifecycle()) .Use<ClassX>()); Container = new Container(x => x.For<IClassX>() .HttpContextScoped() .Use<ClassX>()); } }
次の部分に続きます。