誰かがMicrosoft Unityを説明できますか?


157

Unityに関するMSDNの記事(依存性注入、制御の反転)を読んでいますが、簡単な用語(または簡単な例)で説明する必要があると思います。私はMVPCパターンに精通しています(ここではそれを使用しています)が、まだこのUnityのことを本当に理解できていないので、アプリケーション設計の次のステップだと思います。


12
これが「Unity」と同じ名前になっているのが好きなので、Unity Game Engineのものを検索しているときに、この古い技術をため息が見えます。良いバンド名はすべて採用されていると思います。
Tom Schulz、

2
@ tom-schulz古い技術? nuget.org/packages/Unity-最終更新5日前
ロジャー・ウィルコックス

回答:


174

UnityはIoCの「コンテナー」です。Google StructureMapを試してみてください。IoCが初めての場合は、少し簡単に理解できます。

基本的に、IoCを理解していれば、オブジェクトが作成されたときのコントロールが反転していることがわかります。

IoCなし:

public class MyClass
{
   IMyService _myService; 

   public MyClass()
   {
      _myService = new SomeConcreteService();    
   }
}

IoCコンテナの場合:

public class MyClass
{
   IMyService _myService; 

   public MyClass(IMyService myService)
   {
      _myService = myService;    
   }
}

IoCがない場合、IMyServiceに依存するクラスは、使用するサービスの具象バージョンを更新する必要があります。そして、それは多くの理由で悪いです(クラスをIMyServiceの特定の具体的なバージョンに結合している、簡単にユニットテストできない、簡単に変更できない、など)。

IoCコンテナを使用して、これらの依存関係を解決するようにコンテナを「構成」します。そのため、コンストラクターベースの注入スキームでは、IMyService依存関係へのインターフェイスをコンストラクターに渡すだけです。コンテナーでMyClassを作成すると、コンテナーはIMyService依存関係を解決します。

StructureMapを使用して、コンテナーを構成すると、次のようになります。

StructureMapConfiguration.ForRequestedType<MyClass>().TheDefaultIsConcreteType<MyClass>();
StructureMapConfiguration.ForRequestedType<IMyService>().TheDefaultIsConcreteType<SomeConcreteService>();

つまり、あなたがやったことは、「誰かがIMyServiceをリクエストしたときに、SomeConcreteServiceのコピーを彼らに与える」というコンテナに伝えられます。また、誰かがMyClassを要求すると、具体的なMyClassを取得することも指定しました。

IoCコンテナが実際に行うことはこれだけです。彼らはもっと多くのことができますが、それが目的です-依存関係を解決するので、あなたはする必要がありません(そして、コード全体で "new"キーワードを使用する必要はありません)。

最後のステップ:MyClassを作成すると、次のようになります。

var myClass = ObjectFactory.GetInstance<MyClass>();

お役に立てば幸いです。私に電子メールを送ってください。


2
まるで工場みたいなのかな?私がこれを正しくフォローしている場合、最後の例では<MyClass>の代わりに<IMyClass>を使用しませんか?var myClass = ObjectFactory.GetInstance <IMyClass>()になりますか?あなたの助けをありがとう、これは私にとって良いスタートです!
ライアンアボット

3
ある意味では、工場のようなものです。アプリケーションのマスターファクトリ。ただし、シングルトンを含むさまざまなタイプの多くを返すように構成できます。MyClassへのインターフェイスについては、ビジネスオブジェクトの場合、インターフェイスを抽出しません。他のすべてについては、私は通常そうします。
Chris Holmes

ObjectFactory.GetInstance <MyClass>();のみを呼び出した場合 そして、SomeConcreteClassを構成しなかったのですか?その場合、エラーが発生しますか?
RayLoveless 2013年

1
@Ray:コンテナに依存します。一部のコンテナは、デフォルトで命名規則を使用するように記述されています。たとえば、クラスの名前がMyClassで、インターフェースの名前がIMyInterfaceの場合、コンテナはそのインターフェースのクラスを自動的に構成します。そのため、その場合、手動で構成しないと、コンテナのデフォルトの「規則」がとにかくそれを選択します。ただし、クラスとインターフェイスが規則に従っておらず、そのクラスのコンテナーを構成していない場合、はい、実行時にエラーが発生します。
Chris Holmes

1
@saravanan StructureMapは名前ベースの規則を今やっていると思います。確かではありません。私たちはそれを長い間使用していませんでした(私は私たちのビジネス用にカスタムのものを作成しました。インターフェースとクラスに同じ名前の規則を使用しています)。
Chris Holmes

39

私は、David Haydenによる30分のUnity Dependency Injection IoC Screencastを見て、例を挙げて説明するといいと思いました。これはショーのメモからの抜粋です:

スクリーンキャストは、Unity IoCの一般的な使用法をいくつか示しています。

  • コンテナにないタイプの作成
  • TypeMappingsの登録と解決
  • 名前付きTypeMappingsの登録と解決
  • シングルトン、LifetimeManager、およびContainerControlledLifetimeManager
  • 既存のインスタンスの登録
  • 既存のインスタンスへの依存関係の注入
  • App.config / Web.configによるUnityContainerの生成
  • 依存関係属性ではなく、インジェクションAPIを介した依存関係の指定
  • ネストされた(親子)コンテナの使用

32

Unityは、他の多くのライブラリと同様に、自分で作成しなくても、要求されたタイプのインスタンスを取得できます。そう与えられた。

public interface ICalculator
{
    void Add(int a, int b);
}

public class Calculator : ICalculator
{
    public void Add(int a, int b)
    {
        return a + b;
    }
}

Unityのようなライブラリを使用して、ICalcタイプ(IoC(Inversion of Control))が要求されたときに返されるCalculatorを登録します(この例は理論上のものであり、技術的に正しくありません)。

IoCLlibrary.Register<ICalculator>.Return<Calculator>();

したがって、ICalculatorのインスタンスが必要なときは...

Calculator calc = IoCLibrary.Resolve<ICalculator>();

IoCライブラリは通常、タイプを解決するたびにシングルトンを保持するか、新しいインスタンスを作成するように構成できます。

ここで、ICalculatorに依存して存在できるクラスがあるとします。

public class BankingSystem
{
    public BankingSystem(ICalculator calc)
    {
        _calc = calc;
    }

    private ICalculator _calc;
}

また、ライブラリーをセットアップして、作成時にオブジェクトをコンストラクターに挿入できます。

したがって、DIまたは依存性注入は、別のオブジェクトが必要とする可能性のあるオブジェクトを注入することを意味します。


ICalculatorであるべきですcalc = IoCLibrary.Resolve <ICalculator>();
Shukhrat Raimov


10

UnityはIoCです。IoCのポイントは、型自体の外にある型間の依存関係の配線を抽象化することです。これにはいくつかの利点があります。まず第一に、それは一元的に行われます。つまり、依存関係が変更されたときに多くのコードを変更する必要はありません(単体テストの場合)。

さらに、コードの代わりに構成データを使用してワイヤリングを行う場合、実際にデプロイ後に依存関係を再ワイヤリングできるため、コードを変更せずにアプリケーションの動作を変更できます。



1

ASP.NET Web API 2での依存性注入の例のほとんどをカバーしています

public interface IShape
{
    string Name { get; set; }
}

public class NoShape : IShape
{
    public string Name { get; set; } = "I have No Shape";
}

public class Circle : IShape
{
    public string Name { get; set; } = "Circle";
}

public class Rectangle : IShape
{
    public Rectangle(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; } = "Rectangle";
}

DIAutoV2Controller.csでは自動注入メカニズムが使用されます

[RoutePrefix("api/v2/DIAutoExample")]
public class DIAutoV2Controller : ApiController
{
    private string ConstructorInjected;
    private string MethodInjected1;
    private string MethodInjected2;
    private string MethodInjected3;

    [Dependency]
    public IShape NoShape { get; set; }

    [Dependency("Circle")]
    public IShape ShapeCircle { get; set; }

    [Dependency("Rectangle")]
    public IShape ShapeRectangle { get; set; }

    [Dependency("PiValueExample1")]
    public double PiValue { get; set; }

    [InjectionConstructor]
    public DIAutoV2Controller([Dependency("Circle")]IShape shape1, [Dependency("Rectangle")]IShape shape2, IShape shape3)
    {
        this.ConstructorInjected = shape1.Name + " & " + shape2.Name + " & " + shape3.Name;
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize()
    {
        this.MethodInjected1 = "Default Initialize done";
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize2([Dependency("Circle")]IShape shape1)
    {
        this.MethodInjected2 = shape1.Name;
    }

    [NonAction]
    [InjectionMethod]
    public void Initialize3(IShape shape1)
    {
        this.MethodInjected3 = shape1.Name;
    }

    [HttpGet]
    [Route("constructorinjection")]
    public string constructorinjection()
    {
        return "Constructor Injected: " + this.ConstructorInjected;
    }

    [HttpGet]
    [Route("GetNoShape")]
    public string GetNoShape()
    {
        return "Property Injected: " + this.NoShape.Name;
    }

    [HttpGet]
    [Route("GetShapeCircle")]
    public string GetShapeCircle()
    {
        return "Property Injected: " + this.ShapeCircle.Name;
    }

    [HttpGet]
    [Route("GetShapeRectangle")]
    public string GetShapeRectangle()
    {
        return "Property Injected: " + this.ShapeRectangle.Name;
    }

    [HttpGet]
    [Route("GetPiValue")]
    public string GetPiValue()
    {
        return "Property Injected: " + this.PiValue;
    }

    [HttpGet]
    [Route("MethodInjected1")]
    public string InjectionMethod1()
    {
        return "Method Injected: " + this.MethodInjected1;
    }

    [HttpGet]
    [Route("MethodInjected2")]
    public string InjectionMethod2()
    {
        return "Method Injected: " + this.MethodInjected2;
    }

    [HttpGet]
    [Route("MethodInjected3")]
    public string InjectionMethod3()
    {
        return "Method Injected: " + this.MethodInjected3;
    }
}

DIV2Controller.csでは、すべてがDependency Configuration Resolverクラスから注入されます

[RoutePrefix("api/v2/DIExample")]
public class DIV2Controller : ApiController
{
    private string ConstructorInjected;
    private string MethodInjected1;
    private string MethodInjected2;
    public string MyPropertyName { get; set; }
    public double PiValue1 { get; set; }
    public double PiValue2 { get; set; }
    public IShape Shape { get; set; }

    // MethodInjected
    [NonAction]
    public void Initialize()
    {
        this.MethodInjected1 = "Default Initialize done";
    }

    // MethodInjected
    [NonAction]
    public void Initialize2(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
    {
        this.MethodInjected2 = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
    }

    public DIV2Controller(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
    {
        this.ConstructorInjected = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
    }

    [HttpGet]
    [Route("constructorinjection")]
    public string constructorinjection()
    {
        return "Constructor Injected: " + this.ConstructorInjected;
    }

    [HttpGet]
    [Route("PropertyInjected")]
    public string InjectionProperty()
    {
        return "Property Injected: " + this.MyPropertyName;
    }

    [HttpGet]
    [Route("GetPiValue1")]
    public string GetPiValue1()
    {
        return "Property Injected: " + this.PiValue1;
    }

    [HttpGet]
    [Route("GetPiValue2")]
    public string GetPiValue2()
    {
        return "Property Injected: " + this.PiValue2;
    }

    [HttpGet]
    [Route("GetShape")]
    public string GetShape()
    {
        return "Property Injected: " + this.Shape.Name;
    }

    [HttpGet]
    [Route("MethodInjected1")]
    public string InjectionMethod1()
    {
        return "Method Injected: " + this.MethodInjected1;
    }

    [HttpGet]
    [Route("MethodInjected2")]
    public string InjectionMethod2()
    {
        return "Method Injected: " + this.MethodInjected2;
    }
}

依存関係リゾルバーの構成

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    RegisterInterfaces(container);
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

private static void RegisterInterfaces(UnityContainer container)
{
    var dbContext = new SchoolDbContext();
    // Registration with constructor injection
    container.RegisterType<IStudentRepository, StudentRepository>(new InjectionConstructor(dbContext));
    container.RegisterType<ICourseRepository, CourseRepository>(new InjectionConstructor(dbContext));

    // Set constant/default value of Pi = 3.141 
    container.RegisterInstance<double>("PiValueExample1", 3.141);
    container.RegisterInstance<double>("PiValueExample2", 3.14);

    // without a name
    container.RegisterInstance<IShape>(new NoShape());

    // with circle name
    container.RegisterType<IShape, Circle>("Circle", new InjectionProperty("Name", "I am Circle"));

    // with rectangle name
    container.RegisterType<IShape, Rectangle>("Rectangle", new InjectionConstructor("I am Rectangle"));

    // Complex type like Constructor, Property and method injection
    container.RegisterType<DIV2Controller, DIV2Controller>(
        new InjectionConstructor("Constructor Value1", container.Resolve<IShape>("Circle"), "Constructor Value2", container.Resolve<IShape>()),
        new InjectionMethod("Initialize"),
        new InjectionMethod("Initialize2", "Value1", container.Resolve<IShape>("Circle"), "Value2", container.Resolve<IShape>()),
        new InjectionProperty("MyPropertyName", "Property Value"),
        new InjectionProperty("PiValue1", container.Resolve<double>("PiValueExample1")),
        new InjectionProperty("Shape", container.Resolve<IShape>("Rectangle")),
        new InjectionProperty("PiValue2", container.Resolve<double>("PiValueExample2")));
}

これは、いくつかの理由で特に有用な答えではありません。これは、コードが多すぎてIOCの簡単な説明を提供するには不必要に複雑な例です。その上、コードは実際に必要な場所に明確に文書化されていません。
Dan Atkinson、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.