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);
}
}
どのような優れたコードを実行して、コンストラクターパラメーターの他のオブジェクトインスタンスを提供しますか?
数行のコードでコンテナーを作成できます。その中核は、通常、System.Type
をキーおよび値として使用すると、そのタイプの新しいインスタンスを作成できるオブジェクトになります。単純な実装を書くときSystem.Func<object>
します。次に、いくつかのRegister
メソッドを含む単純な実装を示します。ジェネリックと非ジェネリックの両方のGetInstance
メソッドで、自動配線が可能です。
public class Container
{
Dictionary<Type, Func<object>> registrations = new Dictionary<Type, Func<object>>();
public void Register<TService, TImpl>() where TImpl : TService {
this.registrations.Add(typeof(TService), () => this.GetInstance(typeof(TImpl)));
}
public void Register<TService>(Func<TService> instanceCreator) {
this.registrations.Add(typeof(TService), () => instanceCreator());
}
public void RegisterSingleton<TService>(TService instance) {
this.registrations.Add(typeof(TService), () => instance);
}
public void RegisterSingleton<TService>(Func<TService> instanceCreator) {
var lazy = new Lazy<TService>(instanceCreator);
this.Register<TService>(() => lazy.Value);
}
public object GetInstance(Type serviceType) {
Func<object> creator;
if (this.registrations.TryGetValue(serviceType, out creator)) return creator();
else if (!serviceType.IsAbstract) return this.CreateInstance(serviceType);
else throw new InvalidOperationException("No registration for " + serviceType);
}
private object CreateInstance(Type implementationType) {
var ctor = implementationType.GetConstructors().Single();
var parameterTypes = ctor.GetParameters().Select(p => p.ParameterType);
var dependencies = parameterTypes.Select(t => this.GetInstance(t)).ToArray();
return Activator.CreateInstance(implementationType, dependencies);
}
}
次のように使用できます。
var container = new Container();
container.RegisterSingleton<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ライブラリが提供する多くの重要な機能が不足していますが、 Pure DI (つまり、オブジェクトグラフを手動で配線する)を使用することに勝る利点はありません。何も戻さずに、コンパイル時のサポートを失います。
アプリケーションが小さい場合は、純粋なDIから開始する必要があります。アプリケーションとDI構成が大きくなり、維持が困難になると、 構成ルート が確立されたDIライブラリのいずれかに切り替えることを検討できます。 。
確立されたライブラリと比較して、この素朴な実装には欠けている機能のいくつかを次に示します。
これらの機能により、DI構成を保守可能な状態に保つことができます。
すでに数年前ですが、Ayendeはかつてこれについてブログ投稿を書いています。
15行のコードでIoCコンテナーを構築する
しかし、これは可能な限り非常に単純な実装です。
アインデ自身が 次の投稿 で述べたように、既存のIoCコンテナはクラスインスタンスを返すだけではなく、もっと多くのことを行うことができます-ここが複雑になります。
「Trust me-I'm a Doctor」のコメントですでに述べたように、completeIoCコンテナの実装は簡単ではありません。