几句代码简单实现IoC容器
前言
最近在调试EasyNetQ代码的时候发现里面有一段代码,就是IoC容器的简单实现,跟着他的代码敲了一遍,发现了奇妙之处。当然也是因为我才疏学浅导致孤陋寡闻了。他的思路就是通过动态调用构造函数生成对象,然后将对象保存,调用的时候进行单例调用,而且,代码中不会存在 new 字眼。所有实例对象的创建和映射都在容器中实现。当然,还是用第三方的容器比较稳妥,本文中只是很简单的一个示范。具体理解的是否正确,我也不敢说,只不过,能达到一些预期的效果,功能不够强大。
解析
首先,我们先添加几个接口。IServiceProvider,IServiceRegister,IServiceContainer (继承自前两个)
/// <summary>
/// 服务提供接口
/// </summary>
public interface IServiceProvider
{
/// <summary>
/// 获取某个类型的服务实例
/// </summary>
/// <typeparam name="TService"> TService 接口 </typeparam>
/// <returns>返回TService类型实例</returns>
TService Resolve<TService>() where TService : class;
}
/// <summary>
/// 服务注册接口
/// </summary>
public interface IServiceRegister
{
/// <summary>
/// 注册一个服务工厂
/// </summary>
/// <typeparam name="TService">工厂类</typeparam>
/// <param name="serviceCreator">创建一个新的接口实例</param>
/// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class; /// <summary>
/// 注册服务类型和服务实例
/// </summary>
/// <typeparam name="TService">接口实例</typeparam>
/// <typeparam name="TImplementation">类型继承自TService</typeparam>
/// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
IServiceRegister Register<TService, TImplementation>()
where TService : class
//TImplementation 继承自 TService
where TImplementation : class, TService;
}
/// <summary>
/// 对外接口Container,继承自IServiceProvider,IServiceRegister
/// </summary>
public interface IServiceContainer : IServiceProvider, IServiceRegister { }
当然,最主要的还是 IServiceContainer 的实现,代码中有简单的注释,所以我就直接贴代码了。(看不懂的仔细认真一些就好)
/// <summary>
/// 服务提供类的实现,类似服务工厂
/// </summary>
public class ServiceProvider : IServiceContainer
{
/// <summary>
/// 锁
/// </summary>
private readonly object syncLock = new object();
/// <summary>
/// 存储单例工厂
/// </summary>
private readonly ConcurrentDictionary<Type, object> factories = new ConcurrentDictionary<Type, object>();
/// <summary>
/// 存储注册类型
/// </summary>
private readonly ConcurrentDictionary<Type, Type> registrations = new ConcurrentDictionary<Type, Type>();
/// <summary>
/// 存储实例
/// </summary>
private readonly ConcurrentDictionary<Type, object> instances = new ConcurrentDictionary<Type, object>(); #region 私有方法
/// <summary>
/// 判断服务是否已经被注册过
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
private bool ServiceIsRegistered(Type serviceType)
{
//判断是否在工厂或者注册库内已经注册过该类型
return factories.ContainsKey(serviceType) || registrations.ContainsKey(serviceType);
}
#endregion /// <summary>
/// 注册工厂
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="serviceCreator"></param>
/// <returns></returns>
public IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class
{
lock (syncLock) {
var serviceType = typeof(TService);
if (ServiceIsRegistered(serviceType)) { return this; } //将服务添加到存储器中
factories.TryAdd(serviceType, serviceCreator);
return this;
}
} /// <summary>
/// 注册实例类
/// </summary>
/// <typeparam name="TService">IService 必须实现一个构造器</typeparam>
/// <typeparam name="TImplementation"></typeparam>
/// <returns></returns>
public IServiceRegister Register<TService, TImplementation>() where TService : class where TImplementation : class, TService
{
lock (syncLock)
{
var serviceType = typeof(TService);
var implType = typeof(TImplementation); if (ServiceIsRegistered(serviceType)) { return this; } //如果注册的类不是继承自TService接口,抛出异常
if (!serviceType.IsAssignableFrom(implType))
{
throw new Exception(string.Format("类型 {0} 不继承接口 {1}", implType.Name, serviceType.Name));
}
//获取构造方法,必须只有一个构造方法(why?)
var constructors = implType.GetConstructors();
if (constructors.Length != )
{
throw new Exception(string.Format("服务实例必须有且只有一个构造方法.当前实例 {0} 拥有 {1} 个", implType.Name, constructors.Length.ToString()));
}
//添加
registrations.TryAdd(serviceType, implType);
return this;
}
} /// <summary>
/// 返回一个实例
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
public virtual TService Resolve<TService>() where TService : class
{
var serviceType = typeof(TService);
object service;
//如果实例存储器中已经存在该实例,直接返回
if (instances.TryGetValue(serviceType, out service))
return (TService)service;
lock (syncLock)
{
//加锁,再次判断
if (instances.TryGetValue(serviceType, out service))
{
return (TService)service;
} //如果注册器中存在该类型,创建该实例,并加入到实例存储器中
if (registrations.ContainsKey(serviceType))
{
var implementationType = registrations[serviceType];
service = CreateServiceInstance(implementationType);
instances.TryAdd(serviceType, service);
}
else if (factories.ContainsKey(serviceType))
{
service = ((Func<IServiceProvider, TService>)factories[serviceType])(this);
instances.TryAdd(serviceType, service);
}
else
{
throw new Exception(string.Format("服务类型 {0} 未注册", serviceType.Name));
}
return (TService)service;
}
} private object Resolve(Type serviceType)
{
return GetType()
.GetMethod("Resolve", new Type[])
.MakeGenericMethod(serviceType)
.Invoke(this, new object[]);
} private object CreateServiceInstance(Type implementationType)
{
//获取构造器
var constructors = implementationType.GetConstructors();
//遍历构造器中的参数类型,获取参数
var parameters = constructors[]
.GetParameters()
.Select(parameterInfo => Resolve(parameterInfo.ParameterType))
.ToArray(); //创建实例方法
return constructors[].Invoke(parameters);
}
}
调用方式
写个测试:IAnimal 接口中有Shout方法。在创建两个类继承自IAnimal,Dog,Cat。 IPeople中有个Pet方法,在创建两个类,Man,Women 继承自IPeople。 其中IPeople中初始化要传入IAnimal的实例,来确定养的宠物是猫还是狗.
代码如下:
public interface IAnimal
{
void Shout();
} public class Dog : IAnimal
{
public void Shout()
{
Console.WriteLine("汪汪汪");
}
} public class Cat : IAnimal
{
public void Shout()
{
Console.WriteLine("喵喵喵");
}
}
public interface IPeople
{
void Pet();
} public class Man : IPeople
{
private IAnimal _animal; public Man(IAnimal animal)
{
_animal = animal;
}
public void Pet()
{
Console.WriteLine("[男]我有一个宠物,它在:");
_animal.Shout();
}
} public class Woman : IPeople
{
private IAnimal _animal; public Woman(IAnimal animal)
{
_animal = animal;
}
public void Pet()
{
Console.WriteLine("[女]我有一个宠物,它在:");
_animal.Shout();
}
}
测试代码
static void Main(string[] args)
{
Func<IServiceContainer> getContainer = () => new ServiceProvider();
IServiceContainer container = getContainer(); //注册
container.Register(_ => container)
//注册Dog实例
.Register<IAnimal, Dog>()
//注册Woman实例
.Register<IPeople, Woman>(); //得到people
var people = container.Resolve<IPeople>();
//调用pet方法
people.Pet(); Console.Read();
}
执行结果:
同样,当我们把 Dog换成Cat
我们把animal的注册去掉,由于people依赖于animal,所以会报错误。
总结
这是一个简单的IoC容器的实现,相信真正的要复杂的很多,不过看EasyNetQ就直接这么用的,我觉得如果需求不高的话,也可以用这种方法。而且,代码看起来清洁了许多,不用自己new对象,而且,内部依赖也可以轻松解决。我该去看看真正的容器是怎么实现的了,byebye
几句代码简单实现IoC容器的更多相关文章
- IoC原理-使用反射/Emit来实现一个最简单的IoC容器
从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...
- 【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...
- 比Spring简单的IoC容器
比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...
- 手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...
- .NET实现一个简单的IOC容器
目录 1.主要细节 2.具体示例 参考及示例代码下载 shanzm-2020年3月17日 20:06:01 1.主要细节 使用反射程序集的方式获取对象的类型 通过反射的方式获取指定类型的的所有公共属性 ...
- 自己动手实现一个简单的 IOC容器
控制反转,即Inversion of Control(IoC),是面向对象中的一种设计原则,可以用有效降低架构代码的耦合度,从对象调用者角度又叫做依赖注入,即Dependency Injection( ...
- IL实现简单的IOC容器
既然了解了IL的接口和动态类之间的知识,何不使用进来项目实验一下呢?而第一反应就是想到了平时经常说的IOC容器,在园子里搜索了一下也有这类型的文章http://www.cnblogs.com/kkll ...
- 最简单的ioc容器代码(低仿Spring )
Spring 的一大核心就是IOC,控制反转(依赖注入). 对象交由容器去控制,降低耦合性. Spring 的ioc实现原理其实很简单,容器启动后读取并解析配置文件,根据配置文件中<bean&g ...
- 简单模拟IOC容器:返回对象并能抛出异常
本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该 ...
随机推荐
- Java - 网络IO的阻塞
最近学习时碰到事件驱动和非阻塞的相关知识,随之想到了Java中的Reactor.io与nio的一些东西:在前辈的博客上翻了翻.复习复习,在此记录一番. 实在找不到比较大点的东西,于是随便弄了个压缩包, ...
- Java String、string[]、List初始化方法
String初始化: 1.String str = new String("string1"); 2.String str = "string1"; Strin ...
- linux cut: invalid byte, character or field list Try 'cut --help' for more information.
1. 概述 centos执行简单shell 脚本 报错 cut: invalid byte, character or field listTry 'cut --help' for more info ...
- HTML+CSS+jQuery 纵向导航 && 横向导航 && 消除IE6 BUG && 感悟怎样学习
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...
- C# 修改GroupBox的边框颜色和字体颜色
改变GroupBox边框和的颜色 private void groupBox_BasicInformation_Paint(object sender, PaintEventArgs e) { e.G ...
- C中atoi和strcpy的自定义实现
这是两道经常考到的笔试题,看似简单的实现,其实专注到细节,还是有很多需要注意扣分的地方. atoi实现: #include <iostream> #include<ctype.h&g ...
- 【Web】JavaScript 语法入门
一. 简介 动态性和交互性 1.由浏览器解释执行 2.常见的脚本语言:JavaScript和VBScript P.S. Node.js 是使用JavaScript编写的服务器端框架. 二. JavaS ...
- .net CombinedGeometry的合并模式
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="h ...
- android启动应用
private void openApp(String packageName) { PackageInfo pi = getPackageManager().getPackageInfo(packa ...
- less @import and extend及mixin详解
在less中,通过 @import (keyword) "filename"的方式引入其他的文件,这个keyword可以是以下6种: referrence referrence这个 ...