.NET用の純粋なDI

OOPSOLIDの原則に従うために、依存性注入ライブラリがよく使用されます。これらのライブラリは多数あり、それらはすべて、一連の共通関数によって統合されています。





  • 依存関係グラフを定義するためのAPI





  • オブジェクトの構成





  • オブジェクトライフサイクル管理





私はそれがどのように機能するかを理解することに興味がありました、そしてそれをする最良の方法はあなた自身のIoC.Container依存性注入ライブラリを書くことです。複雑なことを簡単な方法で行うことができます。ジェネリック型でうまく機能します。他の型ではうまく機能しません。インフラストラクチャに依存せずにコードを作成でき、他の同様のソリューションと比較して優れたパフォーマンスを提供しますが、純粋なDIアプローチとは比較できません。





従来の依存性注入ライブラリを使用すると、依存性グラフを簡単に定義でき、パフォーマンスが低下します。これは私たちに妥協を求めることを余儀なくさせます。したがって、多数のオブジェクトを操作する必要がある場合、依存性注入ライブラリを使用すると、アプリケーションの実行が遅くなる可能性があります。ここでのトレードオフの1つは、コードのこの部分でライブラリを使用しないようにし、昔ながらの方法でオブジェクトを作成することです。これにより、事前定義された予測可能なグラフが終了し、そのような特殊なケースごとに、コードの全体的な複雑さが増します。パフォーマンスへの影響に加えて、クラシックライブラリは、設定ミスによるランタイムの問題の原因となる可能性があります。





純粋なDIでは、オブジェクトの構成は手動で行われます。通常、他のコンストラクターを引数として取るコンストラクターがたくさんあります。追加の諸経費はありません。コンパイラーは、構成の正確さをチェックします。オブジェクトの存続期間の管理やその他の問題は、特定の状況に効果的であるか、コードの作成者がより好む方法で発生したときに対処されます。新しいクラスの数が増えるにつれて、または新しい依存関係が増えるたびに、オブジェクトコンポジションコードの複雑さがますます速くなります。ある時点で、この複雑さを制御できなくなる可能性があります。これにより、その後の開発が大幅に遅くなり、エラーが発生します。したがって、私の経験では、コードの量が少ない限り、純粋なDIを適用できます。





これらのアプローチの最良のものだけを保持するとどうなりますか?





  • API





  • DI





  • ""





, . , - . .NET , /API . , JIT.





, - Pure.DI! - , . NuGet beta , :





  • Pure.DI.Contracts API





  • Pure.DI





Pure.DI.Contracts , .NET Framework 3.5, .NET Standard .NET Core , , .NET 5 6, .NET Framework 2, . API, , , C#. API IoC.Container.





.NET 5 source code generator Roslyn Pure.DI. IDE , . “” . , .





, , “” “”:





interface IBox<out T> { T Content { get; } }

interface ICat { State State { get; } }

enum State { Alive, Dead }
      
      



“ ” :





class CardboardBox<T> : IBox<T>
{
    public CardboardBox(T content) => Content = content;

    public T Content { get; }
}

class ShroedingersCat : ICat
{
  //  
  private readonly Lazy<State> _superposition;

  public ShroedingersCat(Lazy<State> superposition) =>
    _superposition = superposition;

  //    
  //        
  public State State => _superposition.Value;

  public override string ToString() => $"{State} cat";
}

      
      



, . DI, SOLID.





, , . Pure.DI.Contracts Pure.DI. “” :





static partial class Glue
{
  //    ,
  //   ,     
  private static readonly Random Indeterminacy = new();

  static Glue()
  {
    DI.Setup()
      //    
      .Bind<State>().To(_ => (State)Indeterminacy.Next(2))
      //     
      .Bind<ICat>().To<ShroedingersCat>()
      //     
      .Bind<IBox<TT>>().To<CardboardBox<TT>>()
      //         
      //   
      .Bind<Program>().As(Singleton).To<Program>();
  }
}

      
      



Setup()



DI “”. static partial , , “DI”. Setup()



string . “Indeterminacy”, Glue static partial, .





Setup()



Bind<>()



To<>()



, :





.Bind().To()







ICat - , , .NET . ShroedingersCat - , .NET . , , . - , . , Bind<>()



, To<>()



. :





  • Bind<>()



    ,





  • As(Lifetime)



    , ,





  • Tag(object)



    , , ,





, :





  • Transient - ,





  • Singleton - ,





  • PerThread -





  • PerResolve -





  • Binding - ILifetime





, , . , :





.Bind().Tag(“Fat”).Tag(“Fluffy”).To()







, Bind<>()



To<>()



- . , . , , typeof(IBox<>)



API , “TT”. - IBox<TT>



, CardboardBox<TT>



. ? , . TT, TT1, TT2 .. API . . c , [GenericTypeArgument]



, :





[GenericTypeArgument]
public class TTMy: IMyInterface { }
      
      



To<>()



. . , “ ” . [Obsolete]



. , , , - . To<>(factory)



. , ,





.Bind<IBox>().To<CardboardBox>()











.Bind<IBox>().To(ctx => new CardboardBox(ctx.Resolve()))







To<>(factory)



lambda , . lambda , - ctx, . ctx.Resolve()



TT . Resolve()



, - object.





!





class Program
{
  //      
  public static void Main() => Glue.Resolve<Program>().Run();

  private readonly IBox<ICat> _box;

  internal Program(IBox<ICat> box) => _box = box;

  private void Run() => Console.WriteLine(_box);
}
      
      



void Main()



Glue.Resolve<Program>()



. Composition Root, , , , , . Resolve<>()



:





static class ProgramSingleton
{
  static readonly Program Shared = 
    new Program(
      new CardboardBox<ICat>(
        new ShroedingersCat(
          new Lazy<State>(
            new Func<State>(
              (State)Indeterminacy.Next(2))))));
}
      
      



, Program Singleton Resolve<>()



Program . , Shared



ProgramSingleton, Glue.





, . ,





ShroedingersCat(Lazy<State> superposition)







Lazy<>



.NET. , Lazy<>



? , Pure.DI BCL Lazy<>, Task<>, Tuple<..>



, . , . DependsOn()



, , .





, ? - Func<>



, BCL . , ICat



, - Func<ICat>



, .





. , . , IEnumerable<ICat>,



ICat[]



.NET, IReadOnlyCollection<T>



. , IEnumerable<ICat>



.





, , API . To<>(factory)



c lambda , , .





, , - . API . , , , TagAttribute:





  • : .Bind<ICat>().Tag(“Fat”).Tag(“Fluffy”).To<FatCat>()







  • : BigBox([Tag(“Fat”)] T content) { }







TagAttribute :





  • TypeAttribute - , , , ,





  • OrderAttribute - , /





  • OrderAttribute -





, , Pure.DI.Contracts. , , , . , :





  • TypeAttribute<>()







  • TagAttribute<>()







  • OrderAttribute<>()







, - : , , . 0, , . , , , “InjectAttribute”, , .





. , Roslyn API, IDE , . . , IDE , , . . , , , . , fallback : IFallback



. Resolve<>()



依存関係が見つからない場合は常に呼び出されます。作成されたオブジェクトをインジェクション用に返すか、例外をスローするか、null返してデフォルトの動作を終了します。ときにフォールバック戦略が装着され、発電機は、状況があなたのコントロール下にあると仮定して、警告をエラーに変更し、コードがコンパイル可能になります。





このライブラリがお役に立てば幸いです。コメントやアイデアは大歓迎です。








All Articles