一个开源的快速IOC容器分享

clark523 2013-10-23 05:59:09
加精
一个效率是Unity 5-7倍的IOC容器分享。
测试效果看上传的图片

源码地址
https://github.com/mt830813/IOCFactory
示例地址
https://github.com/mt830813/IOCFactory/tree/master/IOCFactoryUnitTest

目前已经用在公司内部的一个项目中。
支持json与unitySetting.xml两种文件格式的注册

由于是个人处于兴趣写的。所以有不足之处欢迎批评与建议。
对于性能方面的建议尤其欢迎。
...全文
2173 75 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
75 条回复
切换为时间正序
请发表友善的回复…
发表回复
clark523 2013-10-31
  • 打赏
  • 举报
回复
引用 63 楼 ycg_893 的回复:
你这个存在严重问题:

  var factory = IOCFactory.Factory.GetInst();
            //使用 老赵的 代码计时

            IOrders Orders = null;

            CodeTimer.Time("IOCFactory 无构造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order");
                });
            
            //Orders.OrdersID 应该等于0,没有问题

            CodeTimer.Time("IOCFactory 1个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 1);
                });


            /* 同一实现对象的不同的构造 上下文 context.HashCode 是相同的,所有调用了首次生成的委托。
             * Orders.OrdersID 应该等于1,但等于 = 0,即只调用首次生成对象的委托,虽然生成新的实例,但即是调用首次的创建对象的委托
             * 这种在实际应用中要出问题的
             */ 
            

            CodeTimer.Time("IOCFactory 2个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 2, "张三");
                });

            //Orders.OrdersID 应该等于2,只可惜还是0

            CodeTimer.Time("IOCFactory 3个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 3, "张三", 9.0);
                    
                });

            //Orders.OrdersID 应该等于3,但等于 = 0
另外:NormalInstCreator 这个类的可更新一下 将 Dictionary 改成 ConcurrentDictionary

 private static Func<int, ObjectActivator> BuilderObjectActivator(RegistObjectContext context, params object[] param)
        {
            return (HashCode) =>
            {
                var types = new Type[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    types[i] = param[i].GetType();
                }
                var constructor = context.ObjType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, types, null);
                return NormalInstCreator2.GetActivator(context.ObjType, constructor);
            };
        }
public object CreateInst(RegistObjectContext context, params object[] param)
        {
            // var fun = BuilderObjectActivator(context, param);
            // fun(context.HashCode); 
            ObjectActivator objectCreater = dicCache.GetOrAdd(context.HashCode, BuilderObjectActivator(context, param));
            return objectCreater(param);
        }

感谢你的回复以及支持。 但是目前我有几个疑问。 首先。经过我的测试,你说的第一个问题并不存在,所以我不知道是否是你注册的时候的问题。请帖上你的对象注册代码。 第二。为什么要用ConCurrentDictionary?请详细说明一下。如果能有实际的证例将会更好。
clark523 2013-10-31
  • 打赏
  • 举报
回复
何况比较,不是比较过程,而是比较结果。 我们用IOC的目的是什么?是实现实例化的控制反转以及依赖注入。 所以我们比较的肯定是实例化相同数量的实例,谁快必然谁的性能高。 我是做自己的IOC容器,又不是说Copy的Untiy的IOc容器。我有些东西为什么就一样要完全的Untiy一样呢? 何况多一个多构造的支持也并没有增加多少复杂度。我只是认为没有必要而已。 如果说比较过程的话, A点到B点,从直线来说有2种方法。 1、A正方向到B. 2、A反方向绕地球到C后再从C到B。 按照你的所谓复杂度不一样没有可比性来说, 第一种方法和第二种方法因为复杂度不一样。 所以1方法和2方法的从A到B的速度方面没有可比性。
clark523 2013-10-31
  • 打赏
  • 举报
回复
我都说了我支持任意构造,但是只支持单构造。也就是说 A类可以用2参构造 B类可以用5参构造 但是A类不能用了2参又用1参。因为我这里不支持。也没有必要支持。。。 因为2参就可以包含1参,并且你在DI的时候只可能用到其中一个构造,不可能同时在DI中一会用这个构造一会用另外一个构造。
ycg_893 2013-10-31
  • 打赏
  • 举报
回复
因为Unity等一些组件是支持不同构造的,所以你这样比较性能肯定没有可比性。因为复杂度不一样。
ycg_893 2013-10-31
  • 打赏
  • 举报
回复
只要找到匹配度最高的就可以了, System.Activator.CreateInstance(typeof(User), 1, 3, 5); 这个也只找到匹配最高的。
clark523 2013-10-31
  • 打赏
  • 举报
回复
至于为什么我会捕捉异常后才加锁,那是因为,如果我不对我的缓存池进行写操作的话,没有必要进行锁操作。这也相对于你之前的无论如何都锁一下的多线程性能要好。。(你的那种写法基本上就是将多线程强制转为单线程)。
clark523 2013-10-31
  • 打赏
  • 举报
回复
[quote=引用 69 楼 ycg_893 的回复:] 最近也是重点研究一下IoC与AOP所以对楼主写的东西比较感兴趣,当然开源的也很多,作为开发人员总得明白原理和实现才会用的更好嘛。 其实 Dictionary 与 ConcurrentDictionary 要使用哪个要看情况,ConcurrentDictionary 若多线程同时读取某个键时,并且键都不存在时,它的值委托的确会多次调用,所以在不允许值多次生成的情况下不要使用 ConcurrentDictionary(如用户会话) 而在多读少写高并发的情况下,它的性能比 Dictionary + Lock要好,之所以建议使用 ConcurrentDictionary 是因为这个生成委托是可以多次生成的,并不影响调用结果。 而楼主采用的是 读取不存在产生异常时,并捕捉异常时才加锁创建,其实这还不如直接在外加锁用 TryGetValue 来得快如:

lock (Users)
            {
                string user;
                if (!Users.TryGetValue(1, out user))
                {
                    Users.Add(1, "张三");
                }
            }
因捕捉异常,产生的跟踪所花的时间更长,当然什么都需要测式出来证明,你创建多个线程试一下。 使用哪个字典都不是你这个IoC性能的最大问题,Ioc最大的还是创建对象,特别是不确定的构造参数,这种是比较麻烦的,楼主的这个各种构造参数都只会调用这个(类型 + 别名)的首个编译委托. [quote] trygetValue和tryCatch的性能问题在我的另外一篇帖子中已经证明了。所以我这里不再多说。 下面是这个的链接 http://bbs.csdn.net/topics/390624824 对于不确定参的构造函数,其实我这里是支持不确定参的。 但是我有些地方出于某些特殊的原因(例如委托缓存等如果支持多参将会增加缓存的复杂度。) 并且由于Di的存在,所以我认为多构造函数的情况属于很特殊的例子。并且我们也可以将多构造函数转为1个足够健壮的构造函数.所以只支持单构造函数。 当然,对于普通的实例注册我并没有Check构造函数的数量。这个是一个bug.但是对于装饰者和Di的注册,我都验证了构造函数的数量,如果大于1将会抛出一个异常。
ycg_893 2013-10-31
  • 打赏
  • 举报
回复
最近也是重点研究一下IoC与AOP所以对楼主写的东西比较感兴趣,当然开源的也很多,作为开发人员总得明白原理和实现才会用的更好嘛。 其实 Dictionary 与 ConcurrentDictionary 要使用哪个要看情况,ConcurrentDictionary 若多线程同时读取某个键时,并且键都不存在时,它的值委托的确会多次调用,所以在不允许值多次生成的情况下不要使用 ConcurrentDictionary(如用户会话) 而在多读少写高并发的情况下,它的性能比 Dictionary + Lock要好,之所以建议使用 ConcurrentDictionary 是因为这个生成委托是可以多次生成的,并不影响调用结果。 而楼主采用的是 读取不存在产生异常时,并捕捉异常时才加锁创建,其实这还不如直接在外加锁用 TryGetValue 来得快如:

lock (Users)
            {
                string user;
                if (!Users.TryGetValue(1, out user))
                {
                    Users.Add(1, "张三");
                }
            }
因捕捉异常,产生的跟踪所花的时间更长,当然什么都需要测式出来证明,你创建多个线程试一下。 使用哪个字典都不是你这个IoC性能的最大问题,Ioc最大的还是创建对象,特别是不确定的构造参数,这种是比较麻烦的,楼主的这个各种构造参数都只会调用这个(类型 + 别名)的首个编译委托. 创建一个简单的具有三种构造参数的对象(接口就不用写了)

 public class Orders : IOrders
    {
        public Orders()
        {

        }
        public Orders(int ordersID)
        {
            this.OrdersID = ordersID;
        }
        public Orders(int ordersID, string clientele)
        {
            this.OrdersID = ordersID;
            this.Clientele = clientele;
        }
        public Orders(int ordersID, string clientele, double value)
        {
            this.OrdersID = ordersID;
            this.Clientele = clientele;
            this.Value = value;
        }
        public virtual int OrdersID { get; set; }
        public double Value { get; set; }
        public virtual string Clientele { get; set; }
    }
注册:

Factory factory = Factory.GetInst();
            factory.Regist<IOrders, Orders>("a", IOCFactoryModel.InstType.Normal);

调用:当调用到第二个时就出现异常了,因为首个委托需要三个参数,而第二次调用只要一个参数,所以就出现了索引不存在的异常。因为首个编译的委托需要三个参数。这也就是我所说的,你只缓存了首个调用的构造信息委托。即使第二次不同的构造参数的上下文 Key 也与第一个相同才导致这个结果。

var factory = Factory.GetInst();

            var o1 = factory.Get<IOrders>("a", 1, "张三", 9.0);

            var o2 = factory.Get<IOrders>("a", 1); //产生异常

            if (o2 != null)
            {
            }

其实要实现不仅仅是参数。
 如具有如下构造,并且都能够识别出相关的构造调用,如果性能仍然是前者的 5 倍,那就很不错了。 

   [code=csharp]
    public class U
    {
        public int User { get; set; }
    }

    public class U1:U
    {
        public string Id { get; set; }
    }

    public class Abc
    {
        public Abc(int a, string b)
        {
        }
        public Abc(string a, int b, string c)
        {
        }
        public Abc(int a, string b, string c)
        {
        }
        public Abc(int a, string b, int? c)
        {
        }
        public Abc(int a, U u)
        {
        }       

        public Abc abc(params object[] args)
        {
            //args 要能正确处理参数并解析调用哪个造构的委托。

            return new Abc(1, (string)null);

            return new Abc(1, (U)null);

            return new Abc(1, new U1()); //U1继承U

            return new Abc(null, 1, "");

            return new Abc(1, null, (string)null);

            return new Abc(1, null, (int?)null);  //Nullable<int> 与 int

        }
    }
[/code]
clark523 2013-10-31
  • 打赏
  • 举报
回复
引用 67 楼 ktei2008 的回复:
没仔细读GitHub上的代码——我虽然喜欢用IoC,但是对其如何实现兴趣不大,我只是将其当作工具而已,所以我不随便评论楼主的代码。论坛上有很多人,包括我,特喜欢指点江山,而且是在不看源码的情况下乱点兵,真是没有人品。 如果认同楼主的项目,不妨成为contributors中的一员,你需要做的只是fork, write and make a pull request on github (看不懂英文?谁让你不好好学习的!) 不认同的:爱干嘛干嘛。但是你不读源码,就别评论!
很认同这段话。 并欢迎使用我的Ioc容器。 正在策划下一个开源的东西,想法很多,还没选择好。
ktei2008 2013-10-31
  • 打赏
  • 举报
回复
没仔细读GitHub上的代码——我虽然喜欢用IoC,但是对其如何实现兴趣不大,我只是将其当作工具而已,所以我不随便评论楼主的代码。论坛上有很多人,包括我,特喜欢指点江山,而且是在不看源码的情况下乱点兵,真是没有人品。 如果认同楼主的项目,不妨成为contributors中的一员,你需要做的只是fork, write and make a pull request on github (看不懂英文?谁让你不好好学习的!) 不认同的:爱干嘛干嘛。但是你不读源码,就别评论!
ycg_893 2013-10-31
  • 打赏
  • 举报
回复
你这个存在严重问题:

  var factory = IOCFactory.Factory.GetInst();
            //使用 老赵的 代码计时

            IOrders Orders = null;

            CodeTimer.Time("IOCFactory 无构造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order");
                });
            
            //Orders.OrdersID 应该等于0,没有问题

            CodeTimer.Time("IOCFactory 1个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 1);
                });


            /* 同一实现对象的不同的构造 上下文 context.HashCode 是相同的,所有调用了首次生成的委托。
             * Orders.OrdersID 应该等于1,但等于 = 0,即只调用首次生成对象的委托,虽然生成新的实例,但即是调用首次的创建对象的委托
             * 这种在实际应用中要出问题的
             */ 
            

            CodeTimer.Time("IOCFactory 2个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 2, "张三");
                });

            //Orders.OrdersID 应该等于2,只可惜还是0

            CodeTimer.Time("IOCFactory 3个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 3, "张三", 9.0);
                    
                });

            //Orders.OrdersID 应该等于3,但等于 = 0
另外:NormalInstCreator 这个类的可更新一下 将 Dictionary 改成 ConcurrentDictionary

 private static Func<int, ObjectActivator> BuilderObjectActivator(RegistObjectContext context, params object[] param)
        {
            return (HashCode) =>
            {
                var types = new Type[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    types[i] = param[i].GetType();
                }
                var constructor = context.ObjType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, types, null);
                return NormalInstCreator2.GetActivator(context.ObjType, constructor);
            };
        }
public object CreateInst(RegistObjectContext context, params object[] param)
        {
            // var fun = BuilderObjectActivator(context, param);
            // fun(context.HashCode); 
            ObjectActivator objectCreater = dicCache.GetOrAdd(context.HashCode, BuilderObjectActivator(context, param));
            return objectCreater(param);
        }

clark523 2013-10-31
  • 打赏
  • 举报
回复
对于多线程问题我也考虑过。 不过由于容器的本身性质所在。(一次注册,多次使用。注册不会太多。) 所以多线程我还是打算用锁来保证数据的完整性。 将来如果在实际使用中发现锁会影响性能 或许会考虑使用其他方式来实现。
clark523 2013-10-31
  • 打赏
  • 举报
回复
引用 63 楼 ycg_893 的回复:
你这个存在严重问题:

  var factory = IOCFactory.Factory.GetInst();
            //使用 老赵的 代码计时

            IOrders Orders = null;

            CodeTimer.Time("IOCFactory 无构造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order");
                });
            
            //Orders.OrdersID 应该等于0,没有问题

            CodeTimer.Time("IOCFactory 1个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 1);
                });


            /* 同一实现对象的不同的构造 上下文 context.HashCode 是相同的,所有调用了首次生成的委托。
             * Orders.OrdersID 应该等于1,但等于 = 0,即只调用首次生成对象的委托,虽然生成新的实例,但即是调用首次的创建对象的委托
             * 这种在实际应用中要出问题的
             */ 
            

            CodeTimer.Time("IOCFactory 2个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 2, "张三");
                });

            //Orders.OrdersID 应该等于2,只可惜还是0

            CodeTimer.Time("IOCFactory 3个造参数", iteration, () =>
                {
                    Orders = factory.Get<IOrders>("order", 3, "张三", 9.0);
                    
                });

            //Orders.OrdersID 应该等于3,但等于 = 0
另外:NormalInstCreator 这个类的可更新一下 将 Dictionary 改成 ConcurrentDictionary

 private static Func<int, ObjectActivator> BuilderObjectActivator(RegistObjectContext context, params object[] param)
        {
            return (HashCode) =>
            {
                var types = new Type[param.Length];
                for (int i = 0; i < param.Length; i++)
                {
                    types[i] = param[i].GetType();
                }
                var constructor = context.ObjType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, types, null);
                return NormalInstCreator2.GetActivator(context.ObjType, constructor);
            };
        }
public object CreateInst(RegistObjectContext context, params object[] param)
        {
            // var fun = BuilderObjectActivator(context, param);
            // fun(context.HashCode); 
            ObjectActivator objectCreater = dicCache.GetOrAdd(context.HashCode, BuilderObjectActivator(context, param));
            return objectCreater(param);
        }

查了一下资料,原来ConCurrentDictionary是号称线程安全的Dictionary. 但是其实仔细研究一下就会发现,其实也并不怎么线程安全。 http://www.cnblogs.com/PurpleTide/archive/2011/11/21/2256577.html 上面的链接也许可以帮助你对它有更多的了解。
ycg_893 2013-10-29
  • 打赏
  • 举报
回复
楼主应该实现一接口调用的默认实现类,在业务层大多只调用一种实现。另外带构造参数你没有实现委托编译,而是直接调用 Activator.CreateInstance ,这家伙不带构造参数还好,带构造参数的话那是非常慢呀。 所以你的这个调用只要带构造参数的话性能肯定不行。
hyblusea 2013-10-29
  • 打赏
  • 举报
回复
呃, 这么多年了, 我居然还不知道什么是IOC,恶补下。。。。
clark523 2013-10-29
  • 打赏
  • 举报
回复
引用 58 楼 clark523 的回复:
[quote=引用 57 楼 ycg_893 的回复:] 楼主应该实现一接口调用的默认实现类,在业务层大多只调用一种实现。另外带构造参数你没有实现委托编译,而是直接调用 Activator.CreateInstance ,这家伙不带构造参数还好,带构造参数的话那是非常慢呀。 所以你的这个调用只要带构造参数的话性能肯定不行。
说明你看了我的源码但是并没有仔细看。 首先,我早已支持一接口调用的默认实现类。 其次,你看到的传统普通创建者是已经被Lambda普通创建者替代了的。 再次,我的所有的Get都支持params object[] 这个参数就是说明了我支持带参构造函数。 如果不支持带参构造函数,我怎么注入呢?[/quote] 这个也是我当初性能优化提升最大的一个地方。 也是这里,造成了我的性能比Unity好。
clark523 2013-10-29
  • 打赏
  • 举报
回复
引用 57 楼 ycg_893 的回复:
楼主应该实现一接口调用的默认实现类,在业务层大多只调用一种实现。另外带构造参数你没有实现委托编译,而是直接调用 Activator.CreateInstance ,这家伙不带构造参数还好,带构造参数的话那是非常慢呀。 所以你的这个调用只要带构造参数的话性能肯定不行。
说明你看了我的源码但是并没有仔细看。 首先,我早已支持一接口调用的默认实现类。 其次,你看到的传统普通创建者是已经被Lambda普通创建者替代了的。 再次,我的所有的Get都支持params object[] 这个参数就是说明了我支持带参构造函数。 如果不支持带参构造函数,我怎么注入呢?
yuandaopian2012 2013-10-28
  • 打赏
  • 举报
回复
对于任何底层级别的效率的进步都是有意义的。
慧眼识狗熊 2013-10-28
  • 打赏
  • 举报
回复
做项目中,mark。
qldsrx 2013-10-27
  • 打赏
  • 举报
回复
对于IOC,使用场合总不能确定,也许是你所说的效率问题,以至于我总喜欢使用全局静态变量来传递所需的对象。另外IOC是不是只能通过类型注册呢?如果一个类型要注册2个对象,获取的时候如何区分它们?使用全局静态变量就没这个问题,相同对象多设几个不同的变量名即可。在其他地方给这个全局静态变量初始化,要使用的时候可以判断下是否为null,确信肯定不会为null的话连判断都不用。
加载更多回复(47)
随着Spring框架最新版本——3.0版的发布,Spring平台已经发展成熟,成为Java、Java虚拟机、Groovy、NET或者Action-Script开发人员最强大、最具革命性的解决方案之一。 《Spring攻略(第2版)》是Spring平台的深入指南,它引导你进入Spring 3及其辅助框架的最新技术。《Spring攻略(第2版)》不仅为你全面而又深入地讲解各种概念,并且在每一章中都配备了一系列详细的代码示例,以帮助读者在实际的工作中迅速应用于实战。 SpringSource为核心框架添加了许多部件。这些部件不仅简化了Java EE之上的API,并且为Java EE所忽略的问题提供了第一流的完整解决方案。构建于Spring IoC容器组件模型之上的这些Spring3部件提供了集成、批处理、OSGi、Ajax和Flex集成、状态式的Web应用、REST风格Web服务、富客户端用户界面、Google AppEngine开发、基于云的部署、消息、数据访问、Web服务等多种功能。而且,Spring能很好地与其他辅助框架(包括业务过程管理、群集缓冲以及网格计算)进行协作。 你在寻求和Ruby on Rails一样的一体化架构吗?那么你会被Grails等Spring替代方案所深深吸引,对于Groovy开发人员来说,Grails具有难以置信的能力和生产率。如果你是寻求快速、轻量级的应用构建方法的Java开发人员,你会喜欢上Spring Roo,它能让你快速地通过应用的原型阶段,进入维护阶段,形成清晰的、面向最佳实践的代码。 以上所有这些主题,在这本以丰富代码为基础的攻略中都能找到。我们希望你能够享受Spring平台的学习和使用。 Gary Mak,Josh Long和Daniel Rubio。 作者简介 作者:(美国)麦克(Gary Mak) (美国)隆(Josh Long) (美国)卢比奥(Daniel Rubio) 译者:陈宗恒 姚军 蒋亮 麦克,Gary Mak,Meta-Archit软件技术有限公司的创立者及首席顾问。 隆,Josh Long,SpringSource的Spring开发倡导人。 卢比奥,Daniel Rubio,超过10年的企业级和Web开发经验顾问。

111,088

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧