C#事件.

概念 

一个对象在完成某个工作后或者发生了某种操作后,需要通知其他对象,从而做出反应;发送出去的通知就是事件

场景: 玩家发动范围攻击三名敌人和一名NPC

class Program
{
    static void Main(string[] args)
    {
        Player player = new Player();
        
        Enemy e0 = new Enemy();
        Enemy e1 = new Enemy();
        Enemy e2 = new Enemy();

        NPC npc = new NPC();
        
        player.OnAttack += e0.MinusBlood;
        player.OnAttack += e1.MinusBlood;
        player.OnAttack += e2.MinusBlood;
        player.OnAttack += npc.BeAttackedTest;
    }
}



//使用委托解耦合
//在player当中声明委托类型,将需要调用的减血方法在Player类外设置给内部的委托
class Player
{
   //声明委托,用来规定减血的方法应该符合怎样的方法签名
   //object o —— 谁打的我; attack —— 减了多少血量
   public delegate void OnAttackDelegate(object o, int attack);
   public OnAttackDelegate OnAttack = null;


   //执行方法发动范围攻击
   public void DoAOE()
   {
      if(OnAttack != null)
      {
         OnAttack(this,10); 
      }
   }

   public void Shout()
   {
      Console.WriteLine("玩家很疼");
   }
}


class Enemy
{
   private int blood = 100;
   public void MinusBlood(object o,int attack)  
   {
       Console.WriteLine("好疼!我是Enemy");
       blood -= attack;
       //发动技能  反甲
       Player p = (Player) o;
       p.Shout();
   }
}

  
class NPC
{
   private int blood = 100;
   public void BeAttackedTest(object o,int attack)
   {
       Console.WriteLine("好疼!我是NPC");
       blood -= attack;
   }
}

场景:玩家的范围攻击升级附加毒属性 

class Program
{
    static void Main(string[] args)
    {
        Player player = new Player();
        
        Enemy e0 = new Enemy();
        Enemy e1 = new Enemy();
        Enemy e2 = new Enemy();

        NPC npc = new NPC();
        
        player.OnAttack += e0.MinusBlood;
        player.OnAttack += e1.MinusBlood;
        player.OnAttack += e2.MinusBlood;
        player.OnAttack += npc.BeAttackedTest;
    }
}


//使用委托解耦合
//在player当中声明委托类型,将需要调用的减血方法在Player类外设置给内部的委托
class Player
{
   //声明委托,用来规定减血的方法应该符合怎样的方法签名
   //object o —— 谁打的我;attack —— 减了多少血量;poisoned —— 是否中毒
   public delegate void OnAttackDelegate(object o, int attack ,bool poisoned);
   public OnAttackDelegate OnAttack = null;

   public void DoAOE()
   {
      if(OnAttack != null)
      {
         OnAttack(this,10,true); 
      }
   }

   public void Shout()
   {
      Console.WriteLine("玩家很疼");
   }
}


class Enemy
{
   private int blood = 100;
   public void MinusBlood(object o,int attack,bool poisoned)  //poisoned是否有毒属性攻击
   {
       Console.WriteLine("好疼!我是Enemy");
       blood -= attack;
       //发动技能  反甲
       Player p = (Player) o;
       p.Shout();

       //判断是否中毒
       if(poisoned)
       {
         Console.WriteLine("中毒了!我是Enemy");
       }
   }
}

  
class NPC
{
   private int blood = 100;
   public void BeAttackedTest(object o,int attack,bool poisoned)
   {
       Console.WriteLine("好疼!我是NPC");
       blood -= attack;
   }
}

问题:委托方法,需要携带  的参数太多了,参数还会经常变动(毒属性,以后再加眩晕)

解决办法:将所有参数都包含到一个class里面

class Program
{
    static void Main(string[] args)
    {
        Player player = new Player();
        
        Enemy e0 = new Enemy();
        Enemy e1 = new Enemy();
        Enemy e2 = new Enemy();

        NPC npc = new NPC();
        
        player.OnAttack += e0.MinusBlood;
        player.OnAttack += e1.MinusBlood;
        player.OnAttack += e2.MinusBlood;
        player.OnAttack += npc.BeAttackedTest;
    }
}

//使用委托解耦合
//在player当中声明委托类型,将需要调用的减血方法在Player类外设置给内部的委托
class Player
{
   //声明委托,用来规定减血的方法应该符合怎样的方法签名
   //object o谁发动的攻击;EventArgs 攻击种类集合  
   public delegate void OnAttackDelegate(object o, EventArgs args);
   public OnAttackDelegate OnAttack = null;

   public void DoAOE()
   {
      if(OnAttack != null)
      {
         EventArgs args = new EventArgs();  //给攻击种类赋值
         args.attack = 10;
         args.poisoned = true; 
         OnAttack(this,args); 
      }
   }

   public void Shout()
   {
      Console.WriteLine("玩家很疼");
   }
}

class Enemy
{
   private int blood = 100;
   public void MinusBlood(object o,EventArgs args) 
   {
       Console.WriteLine("好疼!我是Enemy");
       blood -= args.attack;
       //发动技能  反甲
       Player p = (Player) o;
       p.Shout();

       //判断是否中毒
       if(args.poisoned)
       {
         Console.WriteLine("中毒了!我是Enemy");
       }
   }
}
  
class NPC
{
   private int blood = 100;
   public void BeAttackedTest(object o,EventArgs args)
   {
       Console.WriteLine("好疼!我是NPC");
       blood -= args.attack;
   }
}

class EventArgs  //事件参数们
{
   public int attack = 0;          //攻击
   public bool poisoned = false;   //毒属性
}
  • C#写好了事件专用委托定义
//sender  事件的发出者  e  所要执行的事件类
public delegate void EventHandler(object? sender,EventArgs e);
  • 在场景中使用
class Player
{
    public EventHandler OnAttack = null;
}

进一步思考事件

  • 事件对应的委托,不应该被类外界调用,只能由某个操作触发
  • 事件对应的委托,不应该被类外界 直接赋值,只能够通过+、-增减委托方法

关键字  event

public event EventHandler OnAttack = null;

事件规则

  1. 加event关键字修饰的委托,只能够定义在某个类内;
  2. 加event关键字修饰的委托,只能够被当前类内方法触发执行;类外不可触发执行
  3. 加event关键字修饰的委托,只能通过+、-增减委托方法,不可赋值

class Program
{
   static void Main(string[] args)
   {
      Player player = new Player();
      Enemy e = new Enemy();
      
      player.OnAttack += e.AttackMe;

      p.DoAOE();
      //错误示范: 在类外直接调用event修饰的委托去执行,是禁止的
      //player.OnAttack(new object,EventArgs.Empty);
      
   }
}

class Player
{
   //定义Player内部会被触发的事件委托
   public event EventHandler OnAttack = null;

   public void DoAOE()
   {
      if(OnAttack != null)
      {
         OnAttack(this,EventArgs.Empty);
      }
   }
}

class Enemy
{
    public void  AttackMe()
    {
       Console.WriteLine("我被攻击了");
    }

}

事件中的角色

事件参数泛型 

  •  事件被触发的时候,可以传送自定义的事件参数

步骤:

  1. 定义自己的事件参数包class
  2. 将EventHandler特化成传输咱们自己的参数包类的委托类型
  3. 响应方法中,将事件参数包类型替换成我们自己的参数包类型
class Program
{
    static void Main(string[] args)
    {
       Play p = new Player();
       Enemy e = new Enemy();
       p.OnAttack += e.AttackMe;
       p.DoAOE();
    }
}

class Player
{
    //public delegate void EventHandler(object sender,MyArgs e)
    public event EventHandler<MyArgs> OnAttack;

    public void DoAOE()
    {
       if(OnAttack != null)
       {
          MyArgs args = new MyArgs();
          args.attack = 10;
          args.poisoned = true;
          args.headache = true;
          OnAttack(this,args);
       }
    }
}

class Enemy
{
   public void AttackMe(object sender,MyArgs args)
   {
      Console.WriteLine("好疼啊  被攻击了");
      Console.WriteLine("攻击力:" + args.attack);
      Console.WriteLine("是否中毒" + args.poisoned);
      Console.WriteLine("是否眩晕" + args.headache);
   }
}

class MyArgs
{
   public int attack = 0;          //攻击
   public bool poisoned = false;   //中毒
   public bool headache = false;   //眩晕
}

练习一

    public static class Program10
    {
        /*Input Manager 练习
         * 
         *    编写一个InputManager的Class,用来接收用户输入的一个字符,之后调用OnInput事件,
         *    本事件会传导给监听者两个东西(object sender,InputArgs args),args里面携带着用户输入的字符
         */
        public static void Main()  
        {
            InputManager im = new InputManager();
            im.OnInput += OnKeyInput;
            im.WaitForInput();
        }

        static void OnKeyInput(object sender, InputArgs args)
        {
            Console.WriteLine("收到了OnInput事件,拿到了字符:" + args.input);
        }
    }

    class InputManager 
    {
        //public delegate void EventHandler(object sender,InputArgs e)
        public event EventHandler<InputArgs> OnInput;
        public void WaitForInput() 
        {
            while (true) 
            {
                //1.读取用户输入的一个字符
                char input = Convert.ToChar(Console.Read());
                //Console.WriteLine(input);
                //2.调用Onput事件,将字符传导到监听方法/监听者
                if (OnInput != null) 
                {
                    InputArgs args = new InputArgs();
                    args.input = input;
                    OnInput(this, args);
                }
            }
        }
    }

    class InputArgs 
    {
        public char input;
    }

 练习二

编写人类喂食宠物的过程:

  • 事件源:人类,人可以发出喂食的事件
  • 事件参数:喂的什么食物
  • 事件订阅者:狗/猫/ 熊猫
  • 事件处理方法:动物们的OnFeed方法,判断当前食物是否爱吃
  /*喂食宠物
   *   模拟主人喂食宠物的过程,三个宠物狗/猫/熊猫
   *   事件源:主人(OnFeed)
   *   监听者:狗  猫   熊猫  Eat
   *   事件参数包: string  food;
   */
  public static  class Program11
  {
      public static void Main()
      {
          Master master = new Master();
          Dog dog = new Dog();
          Cat cat = new Cat();
          Panda panda = new Panda();
          master.OnFeed += dog.Eat;
          master.OnFeed += cat.Eat;
          master.OnFeed +=  panda.Eat;

          master.FeedAnimals("竹子");
      }

      class Master
      {
          public event EventHandler<FeedArgs> OnFeed;
          public void FeedAnimals(string food) 
          {
              if (OnFeed != null)
              { 
                  FeedArgs args = new FeedArgs();
                  args.food = food;
                  OnFeed(this, args);
              }
          }
      }

      class FeedArgs
      {
          public string food = "";
      }

      class Dog 
      {
          public void Eat(object sender, FeedArgs args) 
          {
              if (args.food != "肉")
              {
                  Console.WriteLine("狗:不爱吃");
              }
              else 
              {
                  Console.WriteLine("狗:爱吃");
              }
          }
      }

      class Cat
      {
          public void Eat(object sender, FeedArgs args)
          {
              if (args.food != "鱼")
              {
                  Console.WriteLine("猫:不爱吃");
              }
              else
              {
                  Console.WriteLine("猫:爱吃");
              }
          }
      }

      class Panda
      {
          public void Eat(object sender, FeedArgs args)
          {
              if (args.food != "竹子")
              {
                  Console.WriteLine("熊猫:不爱吃");
              }
              else
              {
                  Console.WriteLine("熊猫:爱吃");
              }
          }
      }
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值