31

Autofac、Ninjectなどの組み込みまたはライブラリを使用せずに、単純な依存関係リゾルバーを作成するにはどうすればよいですか?

これは私のインタビューの質問でした。

私はこの単純なコードを書きましたが、見栄えが悪いと言われました。非常にハードコードされたアイデアのようなものです。

public interface IRepository { }
interface IDataProvider
{
    List<string> GetData();
}
public class SQLDataProvider : IDataProvider
{
    private readonly IRepository _repository { get; set; }
    public SQLDataProvider(IRepository repository)
    {
        _repository = repository;
    }
    public List<string> GetData()
    {
        return new List<string> { "" };
    }
}
public class MockDataProvider : IDataProvider
{
    public List<string> GetData()
    {
        return new List<string> { "" };
    }
}
class Program
{
 static void Main(string[] args)
 {
    string targetClass = "SQLDataProvider";
    //Here i need to supply IRepository instance too 
   IDataProvider dataProvider = 
   (IDataProvider)Activator.CreateInstance(typeof(IDataProvider), targetClass);

  }
}

コンストラクターパラメーターに他のオブジェクトインスタンスを提供するより良いコードは何ですか?

4

2 に答える 2

46

DI コンテナは複雑なライブラリです。それらを構築するには何年もかかり、それらを維持するには何十年もかかります。しかし、それらの動作を実証するために、わずか数行のコードで単純化された実装を作成できます。

DI コンテナは、通常System.Type、キーとしてディクショナリをラップし、その値は、そのタイプの新しいインスタンスを作成できるオブジェクトになります。あなたが単純な実装を書くときはそうするSystem.Func<object>でしょう。以下は、いくつかのメソッド (ジェネリック メソッドRegisterと非ジェネリック メソッドの両方)を含み、 Auto-WiringGetInstanceを許可する例です。

public class Container
{
    private readonly Dictionary<Type, Func<object>> regs = new();

    public void Register<TService, TImpl>() where TImpl : TService =>
        regs.Add(typeof(TService), () => this.GetInstance(typeof(TImpl)));

    public void Register<TService>(Func<TService> factory) =>
        regs.Add(typeof(TService), () => factory());

    public void RegisterInstance<TService>(TService instance) =>
        regs.Add(typeof(TService), () => instance);

    public void RegisterSingleton<TService>(Func<TService> factory)
    {
        var lazy = new Lazy<TService>(factory);
        Register(() => lazy.Value);
    }

    public object GetInstance(Type type)
    {
        if (regs.TryGetValue(type, out Func<object> fac)) return fac();
        else if (!type.IsAbstract) return this.CreateInstance(type);
        throw new InvalidOperationException("No registration for " + type);
    }

    private object CreateInstance(Type implementationType)
    {
        var ctor = implementationType.GetConstructors().Single();
        var paramTypes = ctor.GetParameters().Select(p => p.ParameterType);
        var dependencies = paramTypes.Select(GetInstance).ToArray();
        return Activator.CreateInstance(implementationType, dependencies);
    }
}

次のように使用できます。

var container = new Container();

container.RegisterInstance<ILogger>(new FileLogger("c:\\logs\\log.txt"));

// SqlUserRepository depends on ILogger
container.Register<IUserRepository, SqlUserRepository>();

// HomeController depends on IUserRepository
// Concrete instances don't need to be resolved
container.GetInstance(typeof(HomeController));

警告:上記のような素朴で単純な実装は絶対に使用しないでください。成熟した DI ライブラリが提供する多くの重要な機能が欠けていますが、純粋な DI (つまり、オブジェクト グラフを手動で接続する)を使用するよりも利点はありません。何も返されずに、コンパイル時のサポートが失われます。

アプリケーションが小さい場合は、純粋な DI から開始する必要があります。アプリケーションと DI 構成が大きくなり、コンポジション ルートの維持が面倒になったら、確立された DI ライブラリの 1 つに切り替えることを検討できます。

確立されたライブラリと比較して、この単純な実装に欠けている機能の一部を次に示します。

  • 自動登録:各タイプを手動で登録する代わりに、一連のタイプを 1 行で登録することにより、Convention over Configuration を適用する機能。
  • インターセプト:さまざまなタイプのデコレーターまたはインターセプターを適用する機能
  • ジェネリック:オープン ジェネリック抽象化をオープン ジェネリック実装にマッピングする
  • 統合:一般的なアプリケーション プラットフォーム (ASP.NET MVC、Web API、.NET Core など) でライブラリを使用する
  • ライフタイム管理:カスタム ライフスタイル (Scoped または Per Request など) を使用して型を登録する機能。
  • エラー処理:循環依存などの構成ミスの検出。この単純な実装では、スタック オーバーフロー例外がスローされます。
  • 検証:構成の正確性を検証し (コンパイル時のサポートの損失を補うため)、一般的な構成の誤りを診断するための機能またはツール。
  • パフォーマンス:この単純な実装を使用すると、大きなオブジェクト グラフの構築が遅くなります (たとえば、大量のガベージが生成されるため、多くの GC プレッシャが発生します)。

これらの機能と機能により、DI コンテナーを使用する際に DI 構成を保守可能に保つことができます。

于 2013-03-30T09:36:33.873 に答える
6

すでに数年前のものですが、Ayende はかつてこれについてブログ記事を書きました:
Building an IoC container in 15 lines of code

しかし、これは可能な限り単純な実装にすぎません。
Ayende 自身は次の投稿で、既存の IoC コンテナは単にクラス インスタンスを返すだけでなく、はるかに多くのことを実行できると述べています。ここが複雑なところです。
「私を信じてください-私は医者です」が彼のコメントですでに述べているように、完全なIoC コンテナーを実装することは簡単なことではありません。

于 2013-03-30T09:23:58.020 に答える