【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


相关文章
|
3月前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
80 5
|
4月前
|
Java 物联网 C#
C#/.NET/.NET Core学习路线集合,学习不迷路!
C#/.NET/.NET Core学习路线集合,学习不迷路!
232 0
|
3月前
|
开发框架 搜索推荐 算法
一个包含了 50+ C#/.NET编程技巧实战练习教程
一个包含了 50+ C#/.NET编程技巧实战练习教程
153 18
|
3月前
|
缓存 算法 安全
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
100 12
|
3月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(24年12月更新)
C#/.NET/.NET Core拾遗补漏合集(24年12月更新)
|
3月前
|
开发框架 算法 .NET
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
|
3月前
|
开发框架 Cloud Native .NET
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
|
4月前
|
开发框架 .NET 程序员
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
Autofac 是一个轻量级的依赖注入框架,专门为 .NET 应用程序量身定做,它就像是你代码中的 "魔法师",用它来管理对象的生命周期,让你的代码更加模块化、易于测试和维护
127 4
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
|
3月前
|
开发框架 监控 .NET
C#进阶-ASP.NET WebForms调用ASMX的WebService接口
通过本文的介绍,希望您能深入理解并掌握ASP.NET WebForms中调用ASMX WebService接口的方法和技巧,并在实际项目中灵活运用这些技术,提高开发效率和应用性能。
109 5
|
3月前
|
算法 Java 测试技术
Benchmark.NET:让 C# 测试程序性能变得既酷又简单
Benchmark.NET是一款专为 .NET 平台设计的性能基准测试框架,它可以帮助你测量代码的执行时间、内存使用情况等性能指标。它就像是你代码的 "健身教练",帮助你找到瓶颈,优化性能,让你的应用跑得更快、更稳!希望这个小教程能让你在追求高性能的路上越走越远,享受编程带来的无限乐趣!
176 13

热门文章

最新文章