C# IOC DI 学习
之前一直不理解IOC DI,今天使劲研究了下,感觉朦朦胧胧有点感觉了,网上的这篇文章对我的有很大的启发
http://www.cnblogs.com/jin-yuan/p/3823559.html
我仔细学习了后,按照自己的习惯从头到尾自己敲了个实例,最后能跑起来了,感觉特高兴,除了用来理解IOC和DI思想,基本没考虑其他,但是还是贴出来记录下吧
1,我们先实现一个简单的读取数据库的功能,由于懒得真的去读数据库了,直接模拟了,首先是一个简单的实体类User
namespace ConsoleApp1 { public class User { public int UserID { get; set; } public string UserName { get; set; } } }
2,然后模拟一个空的DBHelper,只是用来感受IOC的方便,没有真正实现效果,因为要依赖抽象,所以下面的类基本每个都定义了一个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { public interface IDBHelper { int Execute( string sql); } /// <summary> /// 模拟的DBHelper /// </summary> public class DBHelper : IDBHelper { private ILogHelper logHelper; public DBHelper(ILogHelper logHelper) { this .logHelper = logHelper; } public int Execute( string sql) { logHelper.Info( "执行sql:" + sql); return 1; } } } |
3,DBHelper里面有个LogHelper只是一个输出类,模拟工具类,也是为了体验DI的便利性
1 2 3 4 5 6 7 8 9 10 11 12 | using System; namespace ConsoleApp1 { public interface ILogHelper { void Info( string msg); } public class LogHelper : ILogHelper { public void Info( string msg) { Console.WriteLine( "info: " + msg); } } } |
4,然后是模拟的数据访问类,里面用集合模拟数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System.Collections.Generic; namespace ConsoleApp1 { public interface IUserDAL { int Add(User user); List<User> GetUsers(); } public class UserDAL : IUserDAL { private IDBHelper dbHelper; public UserDAL(IDBHelper dbHelper) { this .dbHelper = dbHelper; } public static List<User> users = new List<User>() { new User(){ UserID = 1, UserName = "张三" }, new User(){ UserID =2, UserName = "李四" } }; public int Add(User user) { dbHelper.Execute( "insert into User (UserID,UserName) values (3,'王五')" ); users.Add(user); return 1; } public List<User> GetUsers() { return users; } } } |
5,然后是业务逻辑类,在里面调用数据访问类,以及工具类,如果是传统的写法,这里就要都new一下,既不美观又很繁琐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { public interface IUserBLL { int Add(User user); List<User> GetUsers(); } public class UserBLL : IUserBLL { private IUserDAL userDAL; private ILogHelper logHelper; public UserBLL(IUserDAL userDAL, ILogHelper logHelper) { this .userDAL = userDAL; this .logHelper = logHelper; } public int Add(User user) { logHelper.Info( "UserBLL.Add" ); return userDAL.Add(user); } public List<User> GetUsers() { logHelper.Info( "UserBLL.GetUsers" ); return userDAL.GetUsers(); } } } |
6,模拟是实现的DI管理类,为了好理解,我按照最简单的方式实现的,大佬的例子这里也会考虑IOC,所以比我这复杂一些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | using System; using System.Collections.Generic; using System.Reflection; namespace ConsoleApp1 { /// <summary> /// 简单模拟的DI注入类 /// </summary> public class DIManager { /// <summary> /// 存放关系的容器 /// </summary> private Dictionary<Type, Type> container; public DIManager() { container = new Dictionary<Type, Type>(); } /// <summary> /// 将接口和实现类关联绑定起来 /// </summary> public void Bind<K, V>() { container.Add( typeof (K), typeof (V)); } /// <summary> /// 获取泛型类型的对象 /// </summary> public T Get<T>() { return (T)Injection( typeof (T)); } /// <summary> /// 对传入的类型进行构造函数注入 /// </summary> private object Injection(Type type) { object instance = null ; foreach (ConstructorInfo ci in type.GetConstructors()) { //循环类的构造函数 if (ci.GetParameters().Length > 0) { List< object > parameters = new List< object >(); foreach (ParameterInfo pi in ci.GetParameters()) { //循环构造函数的参数 if (container.ContainsKey(pi.ParameterType)) { parameters.Add(Injection(container[pi.ParameterType])); //递归实现所有相关注册过的类型的构造函数注入 } } instance = CreateInstance(type, parameters.ToArray()); break ; } } if (instance == null ) { instance = CreateInstance(type); } return instance; } /// <summary> /// 创建对象 /// </summary> private object CreateInstance(Type type, params object [] args) { return Activator.CreateInstance(type, args); } } } |
7,最后是Program的使用,我们只要在程序运行的时候,注册绑定需要用到的抽象和实现,然后就能直接通过Get获取实例,并且这些实例中的构造函数都会自动创建注入相关的对象,这样就不用我们各种重复的new了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System; namespace ConsoleApp1 { class Program { static void Main( string [] args) { DIManager manager = new DIManager(); manager.Bind<IUserBLL, UserBLL>(); manager.Bind<IUserDAL, UserDAL>(); manager.Bind<IDBHelper, DBHelper>(); manager.Bind<ILogHelper, LogHelper>(); IUserBLL userBLL = manager.Get<UserBLL>(); User user = new User() { UserID = 3, UserName = "王五" }; Console.WriteLine(userBLL.Add(user)); foreach ( var u in userBLL.GetUsers()) { Console.WriteLine(u.UserName); } Console.ReadKey(); } } } |
总结:IOC是控制反转,就是把底层的耦合抛到外面,类的内部只依赖抽象,代码里定义的那么多接口就是实现这个效果,但是即使我们把控制抛到了外面,这些对象还是得创建啊,所以就用到了DI(依赖注入)上面的类里面都是通过构造函数来获取我们要用到得对象,我们依赖这些对象,对象哪来的?答,注册接口和类得关系,然后在代码里自动生成的,你可以观察DIManager的Injection,大致就是根据类的类型获取构造函数信息,创建构造函数的参数类型的对象,然后根据构造函数以及参数对象创建本身的对象来达到注入的效果,最后递归注入所有相关的构造函数(这里貌似性能浪费啊,假如我只使用UserBLL的一个方法,而这个方法有咩有真正的使用UserDAL和LogHelper,那么程序就创建了白创建了2个用不到的对象,不知道真正的DI是不是解决了这个问题呢)
1 | 依然是朦朦胧胧,继续研究! |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~