65.9K
CodeProject 正在变化。 阅读更多。
Home

15分钟内实现IoC容器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (39投票s)

2011年6月3日

CPOL

4分钟阅读

viewsIcon

85643

downloadIcon

798

如何从头开始创建一个非常简单、基础的IoC容器。

引言

上周,我正在编写一个 Compact Framework 应用程序。我现在非常习惯 IoC,几乎无法想象用另一种方式编程。问题在于 Windsor(我最喜欢的)容器无法在 CF 上运行。所以我查看了我的选择。结果并不多。实际上我找到了一些。其中之一:Ninject,虽然很有前途,但需要一些研究,因为它不是一个容器,而且我当时没有时间。还有另外几个,它们的工作方式与 Windsor 类似,但它们在解析泛型组件时遇到了问题。所以我认为:我要自己动手!!!

变更

  1. 2011 年 6 月 18 日:IService 接口没什么用,如果说有用的话。所以,对它的每次使用都已经被 object 替换。对于第一次看到这篇文章的读者:没关系。
  2. 2011 年 6 月 18 日:改进了泛型注册,之前出现了一些问题,现在已经解决了。
  3. 2012 年 5 月 15 日: 添加了关于 IoC 和 DI 的参考资料。

从头开始构建 IoC

让我们从接口开始

public interface IServicesContainer 
{ 
	TService Resolve<TService>(); 
	object Resolve(Type tService); 
}

public interface IServicesRegistrar : IServicesContainer
{
	void RegisterForAll(params Type[] tServices);
	void RegisterForAll(IEnumerable<Type> tServices);

	void RegisterFor(Type tService, params Type[] tInterfaces);
	void RegisterFor(Type tService, params IEnumerable<Type> tInterfaces);
}
代码摘录 1:IoC 接口

我们需要的一切都在这里。实际的容器 IServicesContainer 和注册器 IServicesRegistrar。一个容器应该同时实现 IServicesContainerIServicesRegistrar,但注册器仅在注册时需要,因此稍后我们可以只使用容器本身。

我们需要一个辅助类型来存储关于类型和实例的信息

internal class ServiceDescriptor 
{ 
	public Type ServiceType { get; set; } 
	public object Instance { get; set; } 
}
代码摘录 2:ServiceDescriptor

让我们从容器开始

internal class ServicesContainer : IServicesRegistrar 
{ 
	private readonly IDictionary<Type, ServiceDescriptor> _services = 
		new Dictionary<Type, ServiceDescriptor>(); 
	
	public TService Resolve<TService>() 
	{ 
		return (TService)Resolve(typeof(TService)); 
	}  
	
	public object Resolve(Type tService) 
	{ 
		return GetInstance(tService);
	}
	...
}
代码摘录 3:容器,大部分

一个字典将保存映射。GetInstance 将查找现有实例或创建一个新实例。让我们继续使用 GetInstance

internal class ServicesContainer : IServicesRegistrar 
{
	...
	private object GetInstance(Type tService)
	{
		if (_services.ContainsKey(tService)) 
			return GetInstance(_services[tService]); 

		var genericDefinition = tService.GetGenericTypeDefinition(); 
		if (_services.ContainsKey(genericDefinition)) 
			return GetGenericInstance(tService, 
				_services[genericDefinition].ServiceType); 

		throw new Exception("Type not registered" + tService);
	}

	...
}
代码摘录 4:GetInstance

有三种情况

  1. 类型已知,如果是这样,我们请求 GetInstance,但这次使用 ServiceDescriptor 作为参数,这个重载将是下一个。
  2. 类型未知,但它可能是一个泛型类型,并且它的泛型类型定义已知,如果是这样,我们要求 GetGenericInstance 来解决这个问题。
  3. 类型未知,你已经看过这部电影了,所以没必要告诉你接下来会发生什么。
internal class ServicesContainer : IServicesRegistrar 
{
	...
	private object GetInstance(ServiceDescriptor serviceDescriptor) 
	{ 
		return serviceDescriptor.Instance ?? ( serviceDescriptor.Instance = 
			CreateInstance(serviceDescriptor.ServiceType)); 
	}
}
代码摘录 5:GetInstance II

这里没什么,只是将一个接口解析为一个具体类,然后要求 CreateInstance 实例化该类。真正有趣的事情发生在 CreateInstance 中,这就是为什么它不得不等待。让我们先看看 GetGenericInstance

internal class ServicesContainer : IServicesRegistar
{
	...
	private object GetGenericInstance(Type tService, Type genericDefinition) 
	{ 
		var genericArguments = tService.GetGenericArguments(); 
		var actualType = genericDefinition.MakeGenericType(genericArguments); 
		var result = CreateInstance(actualType); 

		_services[tService] = new ServiceDescriptor 
		{ 
			ServiceType = actualType, 
			Instance = result 
		}; 

		return result; 
	}
}
代码摘录 6:GetGenericInstance

泛型参数取自实际请求的类型 tService,并且从已注册的类型和这些参数创建一个新的泛型类型。然后我们请求 CreateInstance 来帮助我们。我想是时候了

internal class ServicesContainer : IServicesRegistrar
{
	...
	private object CreateInstance(Type serviceType) 
	{ 
		var ctor = serviceType.GetConstructors().First(); 
		var dependecies = ctor.GetParameters()
			.Select(p => Resolve(p.ParameterType)).ToArray(); 

		return (IService)ctor.Invoke(dependecies); 
	}
}
代码摘录 7:CreateInstance

首先我们获取一个构造函数,应该只有一个。然后我们获取构造函数参数类型(依赖项)并解析它们。最后,我们创建并返回实例。就这样,我们只需要注册类型,我们的 IoC 容器就准备好了。

internal class ServicesContainer : IServicesRegistrar
{
	...
	public ITypeRegistrar RegisterForAll(params Type[] implementations) 
	{ 
		return Register((IEnumerable<Type>)implementations); 
	} 

	public ITypeRegistrar RegisterForAll(IEnumerable<Type> implementations) 
	{ 
		foreach (var impl in implementations) 
			RegisterFor(impl, impl.GetInterfaces()); 

		return this; 
	} 

	public ITypeRegistrar RegisterFor(Type implementation, params Type[] interfaces) 
	{ 
		return RegisterFor(implementation, (IEnumerable<type>)interfaces); 
	} 

	public ITypeRegistrar RegisterFor
		(Type implementation, IEnumerable<type> interfaces) 
	{ 
		foreach (var @interface in interfaces) 
			_services[GetRegistrableType(@interface)] = 
				new ServiceDescriptor 
				{ 
					ServiceType = implementation 
				}; 

		return this; 
	} 

	private static Type GetRegistrableType(Type type) 
	{ 
		return type.IsGenericType && type.ContainsGeneric ? 
				type.GetGenericTypeDefinition() : type; 
	}
}
代码摘录 8:注册器成员

正如你所看到的,有一些变化。ITypeRegistrar 只是 IServicesRegistrar 的一个基类,并且所有 void 成员都已替换为 ITypeRegistrar 以允许方法链。

泛型注册

注册泛型有两种情况,这些情况已通过方法 GetRegistrableType 解决。下一节将展示一个例子。让我们看看这些情况

  1. 注册一个 开放构造类型(未指定泛型参数)。我们称之为“隐式注册”。
  2. 注册一个 封闭构造类型(已指定泛型参数)。我们称之为“显式注册”。

让我们以最简单的方式看看这两种情况

public interface ITypeNamePrinter<TType>
{
	void Print();
}

public class TypeNamePrinter<TType> : ITypeNamePrinter<TType>
{
	public void Print()
	{
		Console.WriteLine(typeof(TType).FullName);
	}
}

[TestFixture]
[TestClass]
public class GenericRegistrations
{
	[Test]
	[TestMethod]
	public void ExplicitRegistration()
	{
		var services = new ServicesContainer();

		services.RegisterForAll(typeof(TypeNamePrinter<int>),
			typeof(TypeNamePrinter<string>), typeof(TypeNamePrinter<Type>));

		services.Resolve<ITypeNamePrinter<int>>().Print();
		services.Resolve<ITypeNamePrinter<string>>().Print();
		services.Resolve<ITypeNamePrinter<Type>>().Print();

		Assert.Throws(typeof(Exception), () => 
			services.Resolve<ITypeNamePrinter<float>>().Print());
	}

	[Test]
	[TestMethod]
	public void ImplicitRegistration()
	{
		var services = new ServicesContainer();

		services.RegisterForAll(typeof(TypeNamePrinter<>));

		services.Resolve<ITypeNamePrinter<int>>().Print();
		services.Resolve<ITypeNamePrinter<string>>().Print();
		services.Resolve<ITypeNamePrinter<Type>>().Print();

		Assert.DoesNotThrow(() => 
			services.Resolve<ITypeNamePrinter<float>>().Print());
	}
}
代码摘录 9:泛型注册

显式实现示例注册每个类型。当请求未显式注册的类型时,将抛出异常。隐式注册仅注册开放构造类型,并且每次您要求容器提供新封闭构造类型的实例时,它都会为该类型创建注册和实例。使用隐式和显式的组合可以产生一个非常灵活和有趣的情况。为了结束这个话题,让我们看一个更复杂一点的例子,使用显式注册。

public interface IExplicit<TType>
{
	void Print();
}

public class StringExplicit : IExplicit<string>
{
	public void Print()
	{
		Console.WriteLine("System.String");
	}
}

public class IntExplicit : IExplicit<int>
{
	public void Print()
	{
		Console.WriteLine("System.Int32");
	}
}

public class TypeExplicit : IExplicit<Type>
{
	public void Print()
	{
		Console.WriteLine("System.Type");
	}
}

[TestFixture]
[TestClass]
public class ExplicitRegistrations
{
	[Test]
	[TestMethod]
	public void Test()
	{
		var services = new ServicesContainer();
		services.RegisterForAll(typeof(StringExplicit), 
			typeof(IntExplicit), typeof(TypeExplicit));

		services.Resolve<IExplicit<int>>().Print();
		services.Resolve<IExplicit<string>>().Print();
		services.Resolve<IExplicit<Type>>().Print();
	}
}
代码摘录 10:显式注册

IoC 王国

我们的新 IoC 容器已经准备好了。让我们玩一下

    public interface IKing : IService
    {
        IBoss<IGuard> Captain { get; }
        IBoss<IMaid> Mistress { get; }

        void RuleTheCastle();
    }

    public interface IServant : IService
    {
        void Serve();
    }

    public interface IGuard : IServant
    {

    }

    public interface IMaid : IServant
    {

    }

    public interface IBoss<TServant>
        where TServant : IServant
    {
        TServant Servant { get; }
        void OrderServantToServe();
    }

    public class King : Service, IKing
    {
        public King(IBoss<IGuard> captain, IBoss<IMaid> mistress)
        {
            Captain = captain;
            Mistress = mistress;
        }

        public void RuleTheCastle()
        {
            Console.WriteLine("Rule!!!");

            Captain.OrderServantToServe();
            Mistress.OrderServantToServe();
        }

        public IBoss<IGuard> Captain
        {
            get;
            private set;
        }

        public IBoss<IMaid> Mistress
        {
            get;
            private set;
        }
    }

    public class Boss<TServant> : Service, IBoss<TServant>
        where TServant : IServant
    {
        public Boss(TServant servant)
        {
            Servant = servant;
        }

        public TServant Servant
        {
            get;
            private set;
        }

        public void OrderServantToServe()
        {
            Servant.Serve();
        }
    }

    public class Guard : Service, IGuard
    {
        public void Serve()
        {
            Console.WriteLine("Watch!!");
        }
    }

    public class Maid : Service, IMaid
    {
        public void Serve()
        {
            Console.WriteLine("Clean!!");
        }
    }
代码摘录 11:国王和城堡

解决方案中包含一个测试项目,它包含这个示例,它是一个虚拟的,但拥有我们测试所需的一切。还包括几个用于注册的类型,但它们太蹩脚了,无法在这里解释。

在告别之前,让我们看看如何使用容器。

var services = new ServicesContainer(); 
services.RegisterForAll(ServicesImplementation .FromAssemblyContaining<IKing>());

var king = services.Resolve<IKing>();
king.RuleTheCastle();
代码摘录 12:使用容器

我希望它会有用!!!享受!!

参考资料 

  1. Martin Fowler,控制反转容器和依赖注入模式 
  2. 维基百科,控制反转 

 

 

© . All rights reserved.