前文

事件注入的想法是由依赖注入所联想到

依赖注入不算什么吸引人的话题 本篇就不详说了 不过有闲暇时间的机会不妨按照自己的兴趣去摸索、研究一些东西,也是一种乐子。

在抓虫系列里简单的描述一下依赖注入在项目中的应用场景抓虫(五) 浅谈依赖注入与控制反转

关于依赖注入推荐T2噬菌体同学的一篇文章 依赖注入那些事儿

关于事件注入已添加进我的设计模式 【系列索引】结合项目实例 回顾传统设计模式 打造属于自己的模式类系列


概要

所谓事件注入是我一时兴起随便杜撰的词,其思想借鉴依赖注入。当然看到这个词很多同学会想到AOP,这里先不置可否。

依赖注入(Dependency Injection),是这样一个过程:由于某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不直接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,然后将其注入到客户类中,保证客户类的正常运行。

也就是说依赖注入在我们的项目场景中充当一个解耦的角色。在项目结构中它可以解耦出一个模块、一个服务。T2噬菌体同学在他的博文中描述了一个游戏打怪的例子来解释依赖注入的作用。那么我们同样用打怪的例子来阐述下事件注入的场景。


 详解

关于原型的设计可以参考T2同学对游戏打怪的描述,我这里直接略过看效果图

下面我们撇开T2同学对依赖注入场景的设计。假设目前这个demo就是可行的。但是随着游戏版本的更新,招式越来越多,效果越来越绚,规则越来越多。T2同学用依赖注入解决的重点是将Role和武器之间的依赖,封装算法簇,让它们之间可以互相替换,让算法的变化独立于使用算法的客户类。

那么浮现问题点,如果我要更新的并不是武器等因素,而是对流程的更新。例如玩dota的同学都知道,一个英雄他的技能前后摇摆的时间也是很重要的因素,好吧,我们给游戏添加技能前摇的设置,砍完怪的我还得获得金币,嗯,再添加一下攻击后获得金币的内容。如何符合我们的OCP原则呢。于是,我们引入事件注入的概念。

首先我们来定义我们所需要的行为

       /// <summary>/// 攻击前事件/// </summary>public static event EventHandler<EventArgs> BeforeAttackEvent;protected virtual void BeforeAttack(EventArgs e){EventHandler<EventArgs> tmp = BeforeAttackEvent;if (tmp != null)tmp(this, e);}/// <summary>/// 攻击后事件/// </summary>public static event EventHandler<GameEventArgs> AttackedEvent;protected virtual void OnAttacked(GameEventArgs e){EventHandler<GameEventArgs> tmp = AttackedEvent;if (tmp != null)tmp(this, e);}

这里定义的仅仅是事件的句柄,如果在这里就实现我们事件的实体也就违背了我们ocp的原则以及事件注入的概念。

这里要提出说明的EventArgs 是包含事件数据的类的基类,如果说我们需要对注入的事件进行额外的信息处理,例如我需要获得金币,那么金币这个属性需要在事件数据中说明

例如上述的攻击后事件

    /// <summary>/// 注入事件元素/// </summary>public class GameEventArgs :EventArgs{public GameEventArgs(): this(0){}public int Coin {get;set;}public GameEventArgs(int coin){Coin = coin;}}

事件的框架有了,我们便在现有程序中找寻合适的注入点。这里我选择的是攻击前后

         /// <summary>         /// 攻击怪物         /// </summary>         /// <param name="monster">被攻击的怪物</param>         public void Attack(Monster monster){BeforeAttack(EventArgs.Empty);if (monster.HP <= 0){Console.WriteLine("此怪物已死");return;}if ("WoodSword" == WeaponTag){monster.HP -= 20;if (monster.HP <= 0){Console.WriteLine("攻击成功!怪物" + monster.Name + "已死亡");}else { Console.WriteLine("攻击成功!怪物" + monster.Name + "损失20HP"); }}else if ("IronSword" == WeaponTag){monster.HP -= 50; if (monster.HP <= 0){Console.WriteLine("攻击成功!怪物" + monster.Name + "已死亡");}else{Console.WriteLine("攻击成功!怪物" + monster.Name + "损失50HP");}}else if ("MagicSword" == WeaponTag){Int32 loss = (_random.NextDouble() < 0.5) ? 100 : 200;monster.HP -= loss;if (200 == loss){Console.WriteLine("出现暴击!!!");}if (monster.HP <= 0){Console.WriteLine("攻击成功!怪物" + monster.Name + "已死亡");}else{Console.WriteLine("攻击成功!怪物" + monster.Name + "损失" + loss + "HP");}}else{Console.WriteLine("角色手里没有武器,无法攻击!");}var e =new GameEventArgs();OnAttacked(e);}

这些设计完成之后,我们需要的就是设计来注入些什么事件。

[Extension("游戏规则_攻击前", "1.0.0.0", "熬夜的虫子")]public class GameRule{public GameRule(){Role.BeforeAttackEvent += BeforeAttack;}void BeforeAttack(object sender, EventArgs e){Console.WriteLine("技能前摇 扭动身体...");               }}

[Extension("游戏规则_攻击后", "1.0.0.0", "熬夜的虫子")]public class GameRule2{private readonly Random _random = new Random();public GameRule2(){Role.AttackedEvent += Attacked;}void Attacked(object sender, EventArgs e){var currentrole = sender as Role;int addcoin = _random.Next(1, 10);if (currentrole != null){currentrole.Coin += addcoin;Console.WriteLine("本次攻击获得了..." + addcoin.ToString() + "个金币,当前金币为" + currentrole.Coin+"个");}}}

事件定义完成后,我们接下来的步骤就是如何来注入到我们现有的框架中。

老道的同学可以发现在事件定义的过程中,我用了扩展属性。没错,这个属性就是实现注入环节的枢纽所在。

/// <summary>/// 事件注入实现/// </summary>[AttributeUsage(AttributeTargets.Class)]public class ExtensionAttribute : Attribute{public ExtensionAttribute(string description, string version, string author){_Description = description;_Version = version;_Author = author;}private readonly string _Description;public string Description{get { return _Description; }}private readonly string _Version;public string Version{get { return _Version; }}private readonly string _Author;public string Author{get { return _Author; }}}

如果想更深入的同学可以在设计一个事件注入管理类,添加一些是否可用,过期时间,版本,描述等等信息来管理注入事件。例如当管理类信息入库,每次注入前check管理类的信息。这样可以可视化并更方便管理注入的事件。

我们回到注入实现这个话题上来,如何利用这个扩展属性,通过反射。

 var di = new System.IO.DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);foreach (var item in di.GetFiles("*.dll", System.IO.SearchOption.TopDirectoryOnly)){System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(item.FullName);Type[] types = assembly.GetTypes();foreach (Type type in types){object[] attributes = type.GetCustomAttributes(typeof(Extension.ExtensionAttribute), false);foreach (object attribute in attributes){assembly.CreateInstance(type.FullName);}}}

上面的程序更具我们定义的扩展属性找到相关的注入事件方法类型,并生成实例。到此,一个简单的注入流程就已经OK了。

我们来看一下效果。

注入事件的组件与源程序分开,源程序不依赖注入事件组件,可以任意的定义多个同类注入事件,将组件放入程序指定的目录即可。

例如我们再新建一个注入事件组件

[Extension("游戏规则_攻击后", "1.0.0.0", "熬夜的虫子")]public class GameRule{public GameRule(){Role.AttackedEvent += Attacked;}void Attacked(object sender, EventArgs e){Console.WriteLine("技能后摆 O(∩_∩)O哈哈哈~...");}}

配置完成后,看下效果


本篇到此 希望对大家有帮助

转载于:https://www.cnblogs.com/dubing/archive/2011/12/21/2295895.html

【C#|.NET】从控制反转(依赖注入)想到事件注入 (非AOP)相关推荐

  1. 那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程...

    那些年,空气中仿佛还能闻到汉唐盛世的余韵,因此你决不允许自己的脸上有油光,时刻保持活力.然而,你一定曾为这些"高深术语"感到过困扰--依赖倒置•控制反转•依赖注入•面向接口编程.也 ...

  2. Spring 的控制反转/依赖注入

    第一章 Spring 的控制反转/依赖注入 回顾 增删改查. 课前测: 本章内容 spring:春天 IOC:将创建对象的权力交给 spring 核心容器去控制.工厂模式 BeanFactory: 懒 ...

  3. 控制反转---依赖注入理解

    在学习Spring的时候,意外找到这个控制反转(IoC)和面向切面(AOP)的容器框架之前,我们先来看一下什么是控制反转(IoC). 控制反转(Ioc)和依赖注入(DI)刚听到感到很难理解,平时也程序 ...

  4. Spring框架学习笔记(1) ---[spring框架概念 , 初步上手使用Spring , 控制反转 依赖注入初步理解 ]

    spring官网 -->spring官网 spring5.3.12–>spring-framework 在线文档 --> Spring 5.3.12 文章目录 1.Spring概论 ...

  5. 设计模式——控制反转依赖注入

    一.控制反转: 从简单的代码示例入手: /// <summary>/// 邮件服务类/// </summary>public class EmailService{public ...

  6. 控制反转-依赖倒置-依赖注入

    控制反转:框架控制应用/组件 依赖倒置: 依赖注入:框架动态注入依赖关系到应用/组件 --------------------------------------------------------- ...

  7. 那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程【转】...

    那些年,空气中仿佛还能闻到汉唐盛世的余韵,因此你决不允许自己的脸上有油光,时刻保持活力.然而,你一定曾为这些"高深术语"感到过困扰.也许时至今日,你仍对它们一知半解.不过就在今天, ...

  8. 高深术语——依赖倒置•控制反转•依赖注入•面向接口编程

    今天,我将带领你以一种全新的高清视角进入奇妙的编程世界,领略涵泳在这些"高深术语"中的活泼泼的地气,以及翩跹于青萍之末的云水禅心. 高聚合·低耦合 简短:管理好自己(内聚),但是有 ...

  9. 【转】那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程

    作者:在好 链接:https://www.zhihu.com/question/31021366/answer/102239756 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

最新文章

  1. 在数组中找出3个数使得它们和为0
  2. rsync的基本操作
  3. boundcolumn 根据值进行判断_Excel使用函数进行条件判断的方法步骤
  4. CGAL4.4+VC2008编译
  5. WebSocket 实现原理
  6. 提前泄露小米10至尊版,博主赔偿100万;传字节跳动实习生删除GB以下所有机器学习模型;​快手宣布7月将取消大小周|极客头条
  7. 集成ueditor后显示html问题处理
  8. 使用cross-env解决跨平台设置NODE_ENV的问题
  9. linux系统下find命令的使用
  10. paip.程序不报错自动退出的解决
  11. 零延时RS-485接口电路的设计与应用
  12. BP神经网络分类算法
  13. 前端用JS + SVG 实现关系图谱 流程图
  14. python测验6_Python语言程序设计 - 测验6: 组合数据类型 (第6周)
  15. python中的opencv读取数字_用python和OpenCV从图像中提取数字
  16. Pdf 转 word 和 word 转 pdf 等
  17. 主力吸筹猛攻指标源码_主力吸筹猛攻指标源码-通达信公式 -程序化交易(CXH99.COM)...
  18. 脱颖而出:为您的独立游戏扩大客户群 游戏运营
  19. 生成器、迭代器、推导式——思维导图
  20. 硕士毕业论文讨论部分怎么写啊?

热门文章

  1. 神经网络与机器学习 笔记—反向传播算法(BP)
  2. hdu4771 水搜索(状态压缩+bfs)
  3. hdu4685 最大匹配可能性
  4. 【Groovy】编译时元编程 ( 编译时元编程引入 | 声明需要编译时处理的类 | 分析 Groovy 类的 AST 语法树 )
  5. 【错误记录】Visual Studio 中配置 NDK 头文件路径
  6. DonkeyID---php扩展-64位自增ID生成器
  7. VB 宏+mysql解决EXCEL表格实现自动化处理
  8. js获取浏览器宽度和高度值
  9. python学习--练习题1、2、3、4、5--
  10. 面向对象程序设计案例