DI murni untuk .NET

Untuk mengikuti prinsip OOP dan SOLID , pustaka injeksi ketergantungan sering digunakan. Ada banyak dari perpustakaan ini, dan semuanya disatukan oleh sekumpulan fungsi umum:





  • API untuk menentukan grafik ketergantungan





  • komposisi benda





  • manajemen siklus hidup objek





Saya tertarik untuk memahami cara kerjanya, dan cara terbaik untuk melakukannya adalah dengan menulis pustaka injeksi ketergantungan IoC.Container Anda sendiri . Ini memungkinkan Anda untuk melakukan hal-hal kompleks dengan cara yang sederhana: ini berfungsi dengan baik dengan tipe umum - yang lain tidak bisa , ini memungkinkan Anda membuat kode, tanpa ketergantungan infrastruktur dan memberikan kinerja yang baik dibandingkan dengan solusi serupa lainnya, tetapi BUKAN dibandingkan dengan pendekatan DI murni.





Dengan menggunakan pustaka injeksi dependensi klasik, kami mendapatkan kesederhanaan dalam menentukan grafik dependensi dan kehilangan performa. Ini memaksa kami untuk mencari kompromi. Jadi, jika Anda perlu bekerja dengan objek dalam jumlah besar, menggunakan library injeksi dependensi dapat memperlambat eksekusi aplikasi. Salah satu pengorbanan di sini adalah menghindari penggunaan perpustakaan di bagian kode ini, dan membuat objek dengan cara lama. Ini akan mengakhiri grafik yang telah ditentukan dan dapat diprediksi, dan setiap kasus khusus seperti itu akan meningkatkan kompleksitas kode secara keseluruhan. Selain dampak kinerja, pustaka klasik dapat menjadi sumber masalah waktu proses karena kesalahan konfigurasi.





Dalam DI murni, komposisi objek dilakukan secara manual: biasanya ada banyak konstruktor yang mengambil konstruktor lain sebagai argumen, dan seterusnya. Tidak ada biaya overhead tambahan. Kompilator memeriksa ketepatan komposisi. Mengelola masa pakai objek atau masalah lainnya ditangani saat muncul dengan cara yang efektif untuk situasi tertentu atau lebih disukai oleh pembuat kode. Dengan bertambahnya jumlah kelas baru, atau dengan setiap ketergantungan baru, kompleksitas kode komposisi objek tumbuh lebih cepat dan lebih cepat. Pada titik tertentu, Anda dapat kehilangan kendali atas kompleksitas ini, yang selanjutnya akan sangat memperlambat pengembangan lebih lanjut dan menyebabkan kesalahan. Oleh karena itu, menurut pengalaman saya, DI murni dapat diterapkan selama jumlah kodenya kecil.





Bagaimana jika kita hanya mempertahankan yang terbaik dari pendekatan ini:





  • 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 : dipanggil setiap kali dependensi tidak dapat ditemukan dan: mengembalikan objek yang dibuat untuk injeksi, melontarkan pengecualian, atau mengembalikan null untuk meninggalkan perilaku default. Ketika strategi fallback dipasang, generator akan mengubah kesalahan menjadi peringatan, dengan asumsi bahwa situasinya berada di bawah kendali Anda, dan kode akan dapat dikompilasi.IFallback



. Resolve<>()







Semoga perpustakaan ini bermanfaat. Setiap komentar dan ide sangat dihargai.








All Articles