序文
ではASP.NETコア、マイクロソフトは、パッケージに対応するデフォルトの依存性注入の実装のセットを提供します。Microsoft.Extensions.DependencyInjection
私たちは、それらに対応するオープンソースの倉庫の実装を見て、見ることができます。この認識に基づいて、我々は明示的に我々のクライアントを作成する必要はありません、統一に注入することができるのServiceProviderオブジェクトに直接取得するために時間を使い、中央集中型のメンテナンスを。オブジェクトの作成と破棄についてあまり心配していないビジネス・ロジックを、書き込み時に私たちをみましょう。これは、なぜ今酷使しないことをお勧めいくつかのベストプラクティスである新しいオブジェクトを取得する方法を。この記事では、私たちは一緒に、独自実装する方法を見ていきますのServiceProviderを。
彼らの手や衣服
区別を容易にするために、私はここと呼ばれるカスタム定義のクラスを持っている:ServiceLocatorは、その機能と公式のServiceProviderは似ています。
基本的に
まず、我々は特定の実現によって上限のための単純なサービス・ディスカバリ・インターフェースを定義する必要があり、次のように、サンプルコードは次のとおりです。
public interface IServiceLocator
{
void AddService<T>();
T GetService<T>();
}
次に、我々は言ったインタフェースの実装クラス、次のサンプルコードの連続を定義します。
public class ServiceLocator : IServiceLocator
{
private readonly IDictionary<object, object> services;
public ServiceLocator()
{
services = new Dictionary<object, object>();
}
public void AddService<T>()
{
services.TryAdd(typeof(T), Activator.CreateInstance<T>());
}
public T GetService<T>()
{
try
{
return (T)services[typeof(T)];
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
したがって、我々はクラスを介して私たちのより多くの集中管理サービスを提供することができ、基本的なサービス発見クラスを実装しました。次のようにここでは便宜上、私は登録のための3つのサービスクラスをシミュレートし、サンプルコードは次のとおりです。
public interface IService
{
void SayHello();
}
public class ServiceA : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from A");
}
}
public class ServiceB : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from B");
}
}
public class ServiceC : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from C");
}
}
次のように使用することは、非常に簡単です:
class Program
{
static void Main(string[] args)
{
IServiceLocator locator = new ServiceLocator();
locator.AddService<ServiceA>();
locator.AddService<ServiceB>();
locator.AddService<ServiceC>();
locator.GetService<ServiceA>().SayHello();
locator.GetService<ServiceB>().SayHello();
locator.GetService<ServiceC>().SayHello();
}
}
以下に示すような結果を実行します。
プログラムがうまく動作しているようですが、結果は我々の期待に沿ったものです。しかし、ほとんどの仕事の経験は、多くの潜在的な問題がある以上を達成するために友人を見つけるでしょう。以下のためにIServiceLocator
インスタンス、我々は一般的に安全委員会を通すように設計されるSingletonパターンを使用して実施されるので、サービスの私たちのリストには、スレッドセーフ場合は必要があります。また、登録を必要と私たちのサービスのあまりに多くは、登録することにより行うことがあれば一度登録当社のサービスは、すぐに行きますので、それがシステムのオーバーヘッドに追加されます、それによって不必要にシステムメモリを消費し、初期化されるので、私たちはさせてください遅れの例は、使用時のみ動作インスタンス化。ここでは、これらの質問を一つずつ改善します。
シングルトン
:シングルトンは、最も簡単なだけでなく、多くの形態の中で最も頻繁に使用されるデザインパターンSingletonパターン自体は、私の以前のブログ記事を参照することができます興味を持っているシングルケースモデル-デザインパターンシリーズここに、私が使用スレッドセーフな方法を私たちの変更ServiceLocator
、加えて、我々はまた、コレクションクラスのスレッドセーフなタイプのために当社のサービスを変更する必要があります。次のようにそのため、全体の修飾は、サンプルコードです。
public class ServiceLocator : IServiceLocator
{
private static ServiceLocator _instance;
private static readonly object _locker = new object();
public static ServiceLocator GetInstance()
{
if (_instance == null)
{
lock (_locker)
{
if (_instance == null)
{
_instance = new ServiceLocator();
}
}
}
return _instance;
}
private readonly IDictionary<object, object> services;
private ServiceLocator()
{
services = new ConcurrentDictionary<object, object>();
}
public void AddService<T>()
{
services.TryAdd(typeof(T), Activator.CreateInstance<T>());
}
public T GetService<T>()
{
try
{
return (T)services[typeof(T)];
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
遅延読み込み
すべての登録されたサービスのサポート遅延ロードを取得し、我々は新しいコレクションを導入する必要があるために、この新しいセットは、適切にアクセスする必要があり、登録我々だけレコード登録タイプの時に対応するオブジェクトの私達のインスタンスを格納するために使用されますサービス提供時に、我々はサービスがインスタンス化されていない見つける必要がある場合、私たちは、オブジェクトをインスタンス化し、返すストアド、その後、我々は、インスタンス化し再、この例ではセットリストを訪問する必要があります。データ構造には、どのような種類のために保存するために使用され、我々は、データ構造の多様性を使用することができ、私は次のようにサンプルコードを保存するために辞書を使用し、まだここにいます:
public class ServiceLocator : IServiceLocator
{
private static ServiceLocator _instance;
private static readonly object _locker = new object();
public static ServiceLocator GetInstance()
{
if (_instance == null)
{
lock (_locker)
{
if (_instance == null)
{
_instance = new ServiceLocator();
}
}
}
return _instance;
}
private readonly IDictionary<Type, Type> servicesType;
private readonly IDictionary<Type, object> instantiatedServices;
private ServiceLocator()
{
servicesType = new ConcurrentDictionary<Type, Type>();
instantiatedServices = new ConcurrentDictionary<Type, object>();
}
public void AddService<T>()
{
servicesType.Add(typeof(T), typeof(T));
}
public T GetService<T>()
{
if (!instantiatedServices.ContainsKey(typeof(T)))
{
try
{
ConstructorInfo constructor = servicesType[typeof(T)].GetConstructor(new Type[0]);
Debug.Assert(constructor != null, "Cannot find a suitable constructor for " + typeof(T));
T service = (T)constructor.Invoke(null);
instantiatedServices.Add(typeof(T), service);
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
return (T)instantiatedServices[typeof(T)];
}
}
構造に一致するので
すべてのサポートサービスのすべての引数なしのコンストラクタ上記の改善が、サービスは参加コンストラクタのために登録している、我々は、上記の反射モードがコンストラクタARGがサポートされていないため、サービスプロバイダは、満たされていない定義します。この場合では、2つのソリューションを持っています。最初はのServiceLocatorて、その後トップへサービスの初期化され、そして楽しい一例の方法を取得し、それを保存するために、我々はそれを呼び出します显示创建
。我々はそれを呼び出す、第二の方法は、反射の仕方によってはまだですが、この反射は複雑に見えるかもしれません隐式创建
。私たちは、コード例の2つの実装のためでした。
- ディスプレイの設定
public interface IServiceLocator
{
void AddService<T>();
//新增接口
void AddService<T>(Func<T> Implementation);
T GetService<T>();
}
public class ServiceLocator : IServiceLocator
{
private static ServiceLocator _instance;
private static readonly object _locker = new object();
public static ServiceLocator GetInstance()
{
if (_instance == null)
{
lock (_locker)
{
if (_instance == null)
{
_instance = new ServiceLocator();
}
}
}
return _instance;
}
private readonly IDictionary<Type, Type> servicesType;
private readonly IDictionary<Type, object> instantiatedServices;
private ServiceLocator()
{
servicesType = new ConcurrentDictionary<Type, Type>();
instantiatedServices = new ConcurrentDictionary<Type, object>();
}
public void AddService<T>()
{
servicesType.Add(typeof(T), typeof(T));
}
//新增接口对应的具体实现
public void AddService<T>(Func<T> Implementation)
{
servicesType.Add(typeof(T), typeof(T));
var done = instantiatedServices.TryAdd(typeof(T), Implementation());
Debug.Assert(done, "Cannot add current service: " + typeof(T));
}
public T GetService<T>()
{
if (!instantiatedServices.ContainsKey(typeof(T)))
{
try
{
ConstructorInfo constructor = servicesType[typeof(T)].GetConstructor(new Type[0]);
Debug.Assert(constructor != null, "Cannot find a suitable constructor for " + typeof(T));
T service = (T)constructor.Invoke(null);
instantiatedServices.Add(typeof(T), service);
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
return (T)instantiatedServices[typeof(T)];
}
}
-------------------------------------------------------------------------------------------
public interface IService
{
void SayHello();
}
public class ServiceA : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from A");
}
}
public class ServiceB : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from B");
}
}
public class ServiceC : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from C");
}
}
public class ServiceD : IService
{
private readonly IService _service;
public ServiceD(IService service)
{
_service = service;
}
public void SayHello()
{
Console.WriteLine("-------------");
_service.SayHello();
Console.WriteLine("Hello,I'm from D");
}
}
-------------------------------------------------------------------------------------------
class Program
{
static void Main(string[] args)
{
IServiceLocator locator = ServiceLocator.GetInstance();
locator.AddService<ServiceA>();
locator.AddService<ServiceB>();
locator.AddService<ServiceC>();
locator.GetService<ServiceA>().SayHello();
locator.GetService<ServiceB>().SayHello();
locator.GetService<ServiceC>().SayHello();
locator.AddService(() => new ServiceD(locator.GetService<ServiceA>()));
locator.GetService<ServiceD>().SayHello();
}
}
プログラムの出力は、以下に示すように:
我々はコンストラクタで登録されたサービスに対応するパラメータを持っている必要がある場合のパラメータは、登録する必要はありませんのServiceLocatorをより柔軟に、そして我々はサービス登録するこの方法を使用することができ、。
- 暗黙の建設
public class ServiceLocator : IServiceLocator
{
private static ServiceLocator _instance;
private static readonly object _locker = new object();
public static ServiceLocator GetInstance()
{
if (_instance == null)
{
lock (_locker)
{
if (_instance == null)
{
_instance = new ServiceLocator();
}
}
}
return _instance;
}
private readonly IDictionary<Type, Type> servicesType;
private readonly IDictionary<Type, object> instantiatedServices;
private ServiceLocator()
{
servicesType = new ConcurrentDictionary<Type, Type>();
instantiatedServices = new ConcurrentDictionary<Type, object>();
}
public void AddService<T>()
{
servicesType.Add(typeof(T), typeof(T));
}
public void AddService<T>(Func<T> Implementation)
{
servicesType.Add(typeof(T), typeof(T));
var done = instantiatedServices.TryAdd(typeof(T), Implementation());
Debug.Assert(done, "Cannot add current service: " + typeof(T));
}
public T GetService<T>()
{
var service = (T)GetService(typeof(T));
if (service == null)
{
throw new ApplicationException("The requested service is not registered");
}
return service;
}
/// <summary>
/// 关键代码
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private object GetService(Type type)
{
if (!instantiatedServices.ContainsKey(type))
{
try
{
ConstructorInfo constructor = servicesType[type].GetTypeInfo().DeclaredConstructors
.Where(constructor => constructor.IsPublic).FirstOrDefault();
ParameterInfo[] ps = constructor.GetParameters();
List<object> parameters = new List<object>();
for (int i = 0; i < ps.Length; i++)
{
ParameterInfo item = ps[i];
bool done = instantiatedServices.TryGetValue(item.ParameterType, out object parameter);
if (!done)
{
parameter = GetService(item.ParameterType);
}
parameters.Add(parameter);
}
object service = constructor.Invoke(parameters.ToArray());
instantiatedServices.Add(type, service);
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
return instantiatedServices[type];
}
}
-------------------------------------------------------------------------------------------
public interface IService
{
void SayHello();
}
public class ServiceA : IService
{
public void SayHello()
{
Console.WriteLine("Hello,I'm from A");
}
}
public class ServiceD : IService
{
private readonly ServiceA _service;
public ServiceD(ServiceA service)
{
_service = service;
}
public void SayHello()
{
Console.WriteLine("-------------");
_service.SayHello();
Console.WriteLine("Hello,I'm from D");
}
}
-------------------------------------------------------------------------------------------
class Program
{
static void Main(string[] args)
{
IServiceLocator locator = ServiceLocator.GetInstance();
locator.AddService<ServiceD>();
locator.AddService<ServiceA>();
locator.GetService<ServiceD>().SayHello();
locator.GetService<ServiceA>().SayHello();
}
}
プログラムの入力は、以下に示すように:
私たちは、動的に暗黙のそれに対応するコンストラクタパラメータの種類に応じて構造、およびこれを作成するために、サービスを経由して登録することが可能
DotNetCore
でのServiceProviderの、非常に同様の方法、それは私たちのサービスの登録順に依存しない、正常であることができます構造。
達成の公式
私たちは自分自身の手によって達成の上のServiceLocatorは、一般的にそれらのアイデアの実現を理解し、正式に実装されている方法を見てする必要があります。
まず、使用中に、私たちは、一般的に次のサンプルコードを使用します。
var services= new ServiceCollection();
......
services.AddSingleton(Configuration.GetSection(nameof(AppSettings)).Get<AppSettings>());
......
ServiceProvider serviceProvider = services.BuildServiceProvider();
あなたは、私たちはを通じて最終的にある、見ることができるのServiceProvider、以下のように当社のサービスは、オブジェクト、公式のソースの実装に対応するクラスを提供取得します:
私たちが見ることができるソースでは、すべてのクライアントはに登録されているIServiceProviderEngineのオブジェクトタイプ、および特定のタイプのオブジェクトが基づいているServiceProviderOptions方法を作成します。ここで、我々はノートに集中する必要がある2つのクラスがあります。
- ServiceProvider
- CallSiteFactory
前者は地面に責任があるの上部注入する責任があるので、あなたが見たい場合は、その後、公式のインスタンスを対応する実装コードを見ることができ、これらのオブジェクトを注入する方法です。
概要
あなたは私の上のすべてのコード例を読めば、彼はそれを書いた場合、ない難しいまったく、実際には、振り返ってみると、あなたも書き出すことができます。しかし実際には、人々が一緒に暮らすことを学ぶことができ、またはめったに、最終的な分析では考え方の問題ではありません。関係者はまた、反射によって達成が、彼は彼の実現につながったいくつかのより厳格な内部ロジックは、物事の過程である、より複雑になるだろう。
余談
MEをクリックします(ガラスの心はクリックしないでください)
私はこの問題について言っても過言ではありませんでしたが、考えるかについて話をする必要があります。
- まず第一に、私の人々はZuifan シニア黒と小さなピンク。友人のこの円は、技術に従事しているので、それらの年初めに、私は単純に、技術界や他の産業が比較的良くと比較することを考えました。数年後、実際に私の考えは、顔を殴られたことがあります。だから今、私は非常に悲しい感じてどのくらいの、人との出会い、いくつかのものを参照してください。過去数年間、真実を伝えるために、私は多くの会った擬似プログラマをそれらのほとんどが好む、イズム、決して自分自身の問題を研究するためのイニシアチブを。他の人が何をすべきかを参照してください、そして、彼らは出会いがないか、または拒否ポットを扱うか、手放すことができない何をすべきか続いています。彼が来ればお聞きしますが、あなたはその後、彼は研究を検索するには時間を取らせ、彼に解決策のアイデアを言って、彼は不幸だった、私はあなたを与えるだろうラベルを。私はあなたがコミュニティに学校の外に作業を開始するとき、あなたの両親に加えて、誰もあなたの自由な専門的な指導をする責任と義務を負わないものとし、学習が独自のもので、すべてが人間の努力に依存し、あると言うしようとしています。言及する価値が、私はあなたが満足している、説明する気にしないでください。
- 第二に、私は個人的にこのことは恥ではないコードをコピーしている感じ。経験の独自の欠如は、学び、理解するために使用ベテランの前に良いコードを、見て、この設計の前任者の長所と短所を感謝し、自分自身にそれを置きます。後でいくつかのビジネスシナリオがそれを学習し、使用することができ遭遇したとき、私はこの動作に異論はない、と私は個人的には、常にこのようにされています。しかし、私は受け入れることができないが、品質に関係なく、他の場所から来盗作の行動の源限り、あなたの記事のインスピレーションを示すものではありません一つのことは、それ以外の場合は何の違いや盗作を行うものではありません、関連するソースを導入するようにしてください。私は本当に私が何を言って、ブログホーム公園の記事のタイトルと内容を見ることだったことを知りません。私は記事へのリンクを指摘するだけでなく、自分自身のために外を見る見ないであろう。要するに、我々はすべての技術的な人々は、この事は酸っぱい遊ぶブログ書いていない願っています。
お互いを奨励します!