重新整理.net core 計1400篇[六] (.net core 一個簡易版的依賴注入容器 )

前言

我們瞭解到一個依賴注入的形式是:

注入依賴服務:var root = new Cat().Register<IFoo, Foo>(Lifetime.Transient);

獲取對應的實例:

GetServices(cat1);

那麼這個是如何實現的呢?

看第一個new Cat()這時候做了什麼?

public class Cat : IServiceProvider, IDisposable
{
internal readonly Cat _root;
internal readonly ConcurrentDictionary<Type, ServiceRegistry> _registries;
private readonly ConcurrentDictionary<Key, object> _services;
private readonly ConcurrentBag<IDisposable> _disposables;
private volatile bool _disposed;

public Cat()
{
	_registries = new ConcurrentDictionary<Type, ServiceRegistry>();
	_root = this;
	_services = new ConcurrentDictionary<Key, object>();
	_disposables = new ConcurrentBag<IDisposable>();
}

這裏可能有一些人沒有接觸過ConcurrentDictionary和 ConcurrentBag,

這兩個分別是線性安全的dictionary和list。是的,因爲我們的ioc可能在不同線程中處理對象,那麼這個時候是需要lock的,但是lock效率並不高,具體怎麼實現的可以去看源碼。

現在我們知道在cat創建的時候呢,會創建一個註冊字典,一個服務字典,一個垃圾回收list。

下面就是去註冊服務:

public static Cat Register<TFrom, TTo>(this Cat cat, Lifetime lifetime) where TTo : TFrom
            => cat.Register(typeof(TFrom), typeof(TTo), lifetime);
具體的實現:
public static Cat Register(this Cat cat, Type from, Type to, Lifetime lifetime)
        {
            Func<Cat, Type[], object> factory = (_, arguments) => Create(_, to, arguments);
            cat.Register(new ServiceRegistry(from, lifetime, factory));
            return cat;
        }

解釋一下過程:創建一個用來工廠去生產to。
new ServiceRegistry(from, lifetime, factory)
將服務註冊對象中中,封裝from,生命週期,和生產to的工廠。

這裏面的作用:

列如GetServices(cat1);,那麼其獲取到Foo的步驟爲,通過IFOO找到對應的註冊服務,然後通過註冊服務去生產Foo。

關注一下cat.Register(new ServiceRegistry(from, lifetime, factory));:

public Cat Register(ServiceRegistry registry)
{
	EnsureNotDisposed();
	if (_registries.TryGetValue(registry.ServiceType, out var existing))
	{
		_registries[registry.ServiceType] = registry;
		registry.Next = existing;
	}
	else
	{
		_registries[registry.ServiceType] = registry;
	}
	return this;
}

這裏面是什麼意思呢?

因爲你可以註冊Register<IFoo, Foo>,那麼你也可以註冊Register<IFoo, Foo1>,也就是說一個IFoo,可以對應多個實例。

當前保存的方式通過鏈表的方式存儲。

這時候其實就註冊完了,那麼使用的時候如何使用?

GetServices(cat1);是如何創建出Foo的呢?

void GetServices<TService>(Cat cat)
{
      cat.GetService<TService>();
}

具體實現:

public object GetService(Type serviceType)
{
	EnsureNotDisposed();

	if (serviceType == typeof(Cat) || serviceType == typeof(IServiceProvider))
	{
		return this;
	}

	ServiceRegistry registry;
	//IEnumerable<T>
	if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
	{
		var elementType = serviceType.GetGenericArguments()[0];
		if (!_registries.TryGetValue(elementType, out registry))
		{
			return Array.CreateInstance(elementType, 0);
		}

		var registries = registry.AsEnumerable();
		var services = registries.Select(it => GetServiceCore(it, Type.EmptyTypes)).ToArray();
		Array array = Array.CreateInstance(elementType, services.Length);
		services.CopyTo(array, 0);
		return array;
	}

	//Generic
	if (serviceType.IsGenericType && !_registries.ContainsKey(serviceType))
	{
		var definition = serviceType.GetGenericTypeDefinition();
		return _registries.TryGetValue(definition, out registry)
			? GetServiceCore(registry, serviceType.GetGenericArguments())
			: null;
	}

	//Normal
	return _registries.TryGetValue(serviceType, out registry)
			? GetServiceCore(registry, new Type[0])
			: null;
}

第一種就是:IEnumerable<>這種,獲取其泛型參數,然後創建IEnumerable<>

第二種是這種:dictionary<string,string>,如果沒有找到的話,那麼會去找:dictionary<Tkey,Tvalue>

第三種就屬於普通模式了,來看下GetServiceCore。

private object GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
{
	var key = new Key(registry, genericArguments);
	var serviceType = registry.ServiceType;

	switch (registry.Lifetime)
	{
		case Lifetime.Root:return GetOrCreate(_root._services, _root._disposables);
		case Lifetime.Self: return GetOrCreate(_services, _disposables);
		default:
			{
				var service = registry.Factory(this, genericArguments);
				if (service is IDisposable disposable && disposable != this)
				{
					_disposables.Add(disposable);
				}
				return service;
			}
	}

	object GetOrCreate(ConcurrentDictionary<Key, object> services, ConcurrentBag<IDisposable> disposables)
	{
		if (services.TryGetValue(key, out var service))
		{
			return service;
		}
		service = registry.Factory(this, genericArguments);
		services[key] = service;
		if (service is IDisposable disposable)
		{
			disposables.Add(disposable);
		}
		return service;
	}
}

上面源碼很好理解:

生成一個key,這個key用來保存當前註冊服務和genericArguments。

保存這個的作用在於處理單例模式。

下面有幾種模式,一種是_root 模式,因爲當前依賴注入可以創建多個實例,但是隻有一個是根實例。

var cat1 = root.CreateChild();

public static Cat CreateChild(this Cat cat) => new Cat(cat);

internal Cat(Cat parent)
{
	_root = parent._root;
	_registries = _root._registries;
	_services = new ConcurrentDictionary<Key, object>();
	_disposables = new ConcurrentBag<IDisposable>();
}

第二種就是:Lifetime.Self

獲取當前cat的單例模式。

第三種就是每次都創建一個。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章