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

简介:

前文

关于依赖注入推荐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 是包含事件数据的类的基类,如果说我们需要对注入的事件进行额外的信息处理,例如我需要获得金币,那么金币这个属性需要在事件数据中说明

例如上述的攻击后事件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// 注入事件元素
/// </summary>
public  class  GameEventArgs :EventArgs
{
     public  GameEventArgs()
         this (0)
     {
     }
 
     public  int  Coin
     {
         get ;
         set ;
     }
 
     public  GameEventArgs( int  coin)
     {
         Coin = coin;
     }
}

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

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

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

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[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+ "个" );
             }
         }
     }

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

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

?
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
/// <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管理类的信息。这样可以可视化并更方便管理注入的事件。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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了。

我们来看一下效果。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
[Extension( "游戏规则_攻击后" "1.0.0.0" "熬夜的虫子" )]
         public  class  GameRule
         {
             public  GameRule()
             {
                 Role.AttackedEvent += Attacked;
             }
 
             void  Attacked( object  sender, EventArgs e)
             {
                 Console.WriteLine( "技能后摆 O(∩_∩)O哈哈哈~..." );
             }
         }

配置完成后,看下效果

 


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

需要源码的同学可以留个邮箱



本文转自 熬夜的虫子  51CTO博客,原文链接:http://blog.51cto.com/dubing/747552


相关文章
|
27天前
|
算法 C# 数据库
【干货】一份10万字免费的C#/.NET/.NET Core面试宝典
C#/.NET/.NET Core相关技术常见面试题汇总,不仅仅为了面试而学习,更多的是查漏补缺、扩充知识面和大家共同学习进步。该知识库主要由自己平时学习实践总结、网上优秀文章资料收集(这一部分会标注来源)和社区小伙伴提供三部分组成。该份基础面试宝典完全免费,发布两年来收获了广大.NET小伙伴的好评,我会持续更新和改进,欢迎关注我的公众号【追逐时光者】第一时间获取最新更新的面试题内容。
|
27天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
2月前
|
SQL 数据库 C#
C# .NET面试系列十一:数据库SQL查询(附建表语句)
#### 第1题 用一条 SQL 语句 查询出每门课都大于80 分的学生姓名 建表语句: ```sql create table tableA ( name varchar(10), kecheng varchar(10), fenshu int(11) ) DEFAULT CHARSET = 'utf8'; ``` 插入数据 ```sql insert into tableA values ('张三', '语文', 81); insert into tableA values ('张三', '数学', 75); insert into tableA values ('李四',
70 2
C# .NET面试系列十一:数据库SQL查询(附建表语句)
|
2月前
|
开发框架 算法 搜索推荐
C# .NET面试系列九:常见的算法
#### 1. 求质数 ```c# // 判断一个数是否为质数的方法 public static bool IsPrime(int number) { if (number < 2) { return false; } for (int i = 2; i <= Math.Sqrt(number); i++) { if (number % i == 0) { return false; } } return true; } class Progr
60 1
|
2月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
107 3
|
5天前
|
开发框架 .NET 中间件
C#/.NET快速上手学习资料集(让现在的自己不再迷茫)
C#/.NET快速上手学习资料集(让现在的自己不再迷茫)
|
7天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
7天前
|
人工智能 前端开发 开发工具
【专栏】.NET 技术:为开发注入新动力
【4月更文挑战第29天】本文探讨了.NET技术如何为开发注入新动力,分为核心优势、创新应用及挑战与机遇三部分。.NET提供统一多语言开发平台、强大的Visual Studio工具、跨平台能力、丰富的类库和活跃社区支持。在现代开发中,它应用于企业级、Web、移动、云服务和游戏开发。然而,面临性能优化、容器化、AI集成等挑战,.NET需持续创新。开发者应深入学习,抓住技术趋势,共同推动.NET技术进步。
|
21天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
|
27天前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
在这个快速发展的技术世界中,时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节,以帮助大家更全面地了解这些技术栈的特性和发展方向。