C#中的委托和事件(三)

事件的由来

  我们继续思考上面的程序:上面的三个方法都定义在Programe类中,这样做是为了理解的方便,实际应用中,通常都是 GreetPeople 在一个类中,ChineseGreeting和 EnglishGreeting 在另外的类中。现在你已经对委托有了初步了解,是时候对上面的例子做个改进了。假设我们将GreetingPeople()放在一个叫 GreetingManager的类中,那么新程序应该是这个样子的:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. namespace Delegate   
  2.   
  3. {          
  4.   
  5.         //定义委托,它定义了可以代表的方法的类型   
  6.   
  7.         public delegate void GreetingDelegate(string name);   
  8.   
  9.         //新建的GreetingManager类   
  10.   
  11.         public class GreetingManager   
  12.   
  13.          {   
  14.   
  15.                public void GreetPeople(string name, GreetingDelegate MakeGreeting)   
  16.   
  17.                  {   
  18.   
  19.                        MakeGreeting(name);   
  20.   
  21.                  }   
  22.   
  23.          }   
  24.   
  25.        class Program   
  26.   
  27.          {   
  28.   
  29.                 private static void EnglishGreeting(string name)   
  30.   
  31.                   {   
  32.   
  33.                        Console.WriteLine("Morning, " + name);   
  34.   
  35.                  }   
  36.   
  37.                 private static void ChineseGreeting(string name)   
  38.   
  39.                  {   
  40.   
  41.                        Console.WriteLine("早上好, " + name);   
  42.   
  43.                  }   
  44.   
  45.                 static void Main(string[] args)   
  46.   
  47.                   {   
  48.   
  49.                         // ... ...   
  50.   
  51.                  }   
  52.   
  53.          }   
  54.   
  55. }  

namespace Delegate{ //定义委托,它定义了可以代表的方法的类型 public delegate void GreetingDelegate(string name); //新建的GreetingManager类 public class GreetingManager { public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } } class Program { private static void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } private static void ChineseGreeting(string name) { Console.WriteLine("早上好, " + name); } static void Main(string[] args) { // ... ... } } }

  这个时候,如果要实现前面演示的输出效果,Main方法我想应该是这样的:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. static void Main(string[] args)   
  2.   
  3. {   
  4.   
  5.          GreetingManager gm = new   GreetingManager();   
  6.   
  7.          gm.GreetPeople("Jimmy Zhang", EnglishGreeting);   
  8.   
  9.          gm.GreetPeople("张子阳", ChineseGreeting);   
  10.   
  11. }   

static void Main(string[] args) { GreetingManager gm = new GreetingManager(); gm.GreetPeople("Jimmy Zhang", EnglishGreeting); gm.GreetPeople("张子阳", ChineseGreeting); }

我们运行这段代码,嗯,没有任何问题。程序一如预料地那样输出了:

Morning, Jimmy Zhang

早上好, 张子阳

  现在,假设我们需要使用上一节学到的知识,将多个方法绑定到同一个委托变量,该如何做呢?让我们再次改写代码:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. static void Main(string[] args)   
  2.   
  3. {   
  4.   
  5.         GreetingManager gm = new   GreetingManager();   
  6.   
  7.         GreetingDelegate delegate1;   
  8.   
  9.         delegate1 = EnglishGreeting;   
  10.   
  11.         delegate1 += ChineseGreeting;   
  12.   
  13.         gm.GreetPeople("Jimmy Zhang", delegate1);   

static void Main(string[] args) { GreetingManager gm = new GreetingManager(); GreetingDelegate delegate1; delegate1 = EnglishGreeting; delegate1 += ChineseGreeting; gm.GreetPeople("Jimmy Zhang", delegate1);}

输出:

Morning, Jimmy Zhang

早上好, Jimmy Zhang

  到了这里,我们不禁想到:面向对象设计,讲究的是对象的封装,既然可以声明委托类型的变量(在上例中是delegate1),我们何不将这个变 量封装到 GreetManager类中?在这个类的客户端中使用不是更方便么?于是,我们改写GreetManager类,像这样:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. public class GreetingManager   
  2.   
  3.   
  4.   
  5.         //在GreetingManager类的内部声明delegate1变量   
  6.   
  7.        public GreetingDelegate delegate1;         
  8.   
  9.        public void GreetPeople(string name, GreetingDelegate MakeGreeting)   
  10.   
  11.         {   
  12.   
  13.                 MakeGreeting(name);   
  14.   
  15.         }   
  16.   
  17. }  

public class GreetingManager{ //在GreetingManager类的内部声明delegate1变量 public GreetingDelegate delegate1; public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } }

现在,我们可以这样使用这个委托变量:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. static void Main(string[] args)   
  2.   
  3. {   
  4.   
  5.         GreetingManager gm = new   GreetingManager();   
  6.   
  7.         gm.delegate1 = EnglishGreeting;   
  8.   
  9.         gm.delegate1 += ChineseGreeting;   
  10.   
  11.         gm.GreetPeople("Jimmy Zhang", gm.delegate1);   
  12.   
  13. }  

static void Main(string[] args){ GreetingManager gm = new GreetingManager(); gm.delegate1 = EnglishGreeting; gm.delegate1 += ChineseGreeting; gm.GreetPeople("Jimmy Zhang", gm.delegate1);}

  尽管这样达到了我们要的效果,但是似乎并不美气,光是第一个方法注册用“=”,第二个用“+=”就让人觉得别扭。此时,轮到Event出场了,C# 中可以使用事件来专门完成这项工作,我们改写GreetingManager类,它变成了这个样子:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. public class GreetingManager   
  2.   
  3. {   
  4.   
  5.         //这一次我们在这里声明一个事件   
  6.   
  7.         public event GreetingDelegate MakeGreet;   
  8.   
  9.        public void GreetPeople(string name, GreetingDelegate MakeGreeting)   
  10.   
  11.         {   
  12.   
  13.                 MakeGreeting(name);   
  14.   
  15.         }   
  16.   
  17. }  

public class GreetingManager{ //这一次我们在这里声明一个事件 public event GreetingDelegate MakeGreet; public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); }}

  很容易注意到:MakeGreet 事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字。看到这里,你差不多明白到:事件其实没什么不好理解的,声明一个事件不过类似于声明一个委托类型的变量而已。

  我们想当然地改写Main方法:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. static void Main(string[] args)   
  2.   
  3.   
  4.   
  5.         GreetingManager gm = new   GreetingManager();   
  6.   
  7.         gm.MakeGreet = EnglishGreeting;   
  8.   
  9.        // 编译错误1   
  10.   
  11.         gm.MakeGreet += ChineseGreeting;   
  12.   
  13.         gm.GreetPeople("Jimmy Zhang", gm.MakeGreet);   
  14.   
  15.        //编译错误2   
  16.   
  17. }  

static void Main(string[] args){ GreetingManager gm = new GreetingManager(); gm.MakeGreet = EnglishGreeting; // 编译错误1 gm.MakeGreet += ChineseGreeting; gm.GreetPeople("Jimmy Zhang", gm.MakeGreet); //编译错误2 }

  这次,你会得到编译错误:事件“Delegate.GreetingManager.MakeGreet”只能出现在 += 或 -= 的左边(从类型“Delegate.GreetingManager”中使用时除外)。

  事件和委托的编译代码

  这时候,我们不得不注释掉编译错误的行,然后重新进行编译,再借助Reflactor来对 event的声明语句做一探究,看看为什么会发生这样的错误:

public event GreetingDelegate MakeGreet;

  可以看到,实际上尽管我们在GreetingManager里将 MakeGreet 声明为public,但是,实际上MakeGreet会被编译成 私有字段,难怪会发生上面的编译错误了,因为它根本就不允许在GreetingManager类的外面以赋值的方式访问。

  我们进一步看下MakeGreet所产生的代码:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. private GreetingDelegate MakeGreet;   
  2.   
  3. //对事件的声明 实际是 声明一个私有的委托变量   
  4.   
  5. [MethodImpl(MethodImplOptions.Synchronized)]   
  6.   
  7. public void add_MakeGreet(GreetingDelegate value)   
  8.   
  9. {   
  10.   
  11.      this.MakeGreet = (GreetingDelegate) Delegate.Combine(this.MakeGreet, value);   
  12.   
  13. }   
  14.   
  15. [MethodImpl(MethodImplOptions.Synchronized)]   
  16.   
  17. public void remove_MakeGreet(GreetingDelegate value)   
  18.   
  19. {   
  20.   
  21.      this.MakeGreet = (GreetingDelegate) Delegate.Remove(this.MakeGreet, value);   
  22.   
  23. }  

private GreetingDelegate MakeGreet; //对事件的声明 实际是 声明一个私有的委托变量[MethodImpl(MethodImplOptions.Synchronized)] public void add_MakeGreet(GreetingDelegate value){ this.MakeGreet = (GreetingDelegate) Delegate.Combine(this.MakeGreet, value); }[MethodImpl(MethodImplOptions.Synchronized)]public void remove_MakeGreet(GreetingDelegate value){ this.MakeGreet = (GreetingDelegate) Delegate.Remove(this.MakeGreet, value);}

  现在已经很明确了:MakeGreet 事件确实是一个GreetingDelegate类型的委托,只不过不管是不是声明为public,它总是被声明为private。另外,它还有两个方 法,分别是add_MakeGreet和remove_MakeGreet,这两个方法分别用于注册委托类型的方法和取消注册,实际上也就是: “+= ”对应 add_MakeGreet,“-=”对应remove_MakeGreet。而这两个方法的访问限制取决于声明事件时的访问限制符。

  在add_MakeGreet()方法内部,实际上调用了System.Delegate的Combine()静态方法,这个方法用于将当前的变量添加到委托链表中。我们前面提到过两次,说委托实际上是一个类,在我们定义委托的时候:

public delegate void GreetingDelegate(string name);

  当编译器遇到这段代码的时候,会生成下面这样一个完整的类:

以下为引用的内容:

view plaincopy to clipboardprint?

  1. public class GreetingDelegate:System.MulticastDelegate   
  2.   
  3. {   
  4.   
  5.        public GreetingDelegate(object @object, IntPtr method);   
  6.   
  7.        public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);   
  8.   
  9.        public virtual void EndInvoke(IAsyncResult result);   
  10.   
  11.        public virtual void Invoke(string name);   
  12.   
  13. }  
  14.  关于这个类的更深入内容,可以参阅《CLR Via C#》等相关书籍,这里就不再讨论了。

内容概要:本文详细探讨了基于MATLAB/SIMULINK的多载波无线通信系统仿真及性能分析,重点研究了以OFDM为代表的多载波技术。文章首先介绍了OFDM的基本原理系统组成,随后通过仿真平台分析了不同调制方式的抗干扰性能、信道估计算法对系统性能的影响以及同步技术的实现与分析。文中提供了详细的MATLAB代码实现,涵盖OFDM系统的基本仿真、信道估计算法比较、同步算法实现不同调制方式的性能比较。此外,还讨论了信道特征、OFDM关键技术、信道估计、同步技术系统级仿真架构,并提出了未来的改进方向,如深度学习增强、混合波形设计硬件加速方案。; 适合人群:具备无线通信基础知识,尤其是对OFDM技术有一定了解的研究人员技术人员;从事无线通信系统设计与开发的工程师;高校通信工程专业的高年级本科生研究生。; 使用场景及目标:①理解OFDM系统的工作原理及其在多径信道环境下的性能表现;②掌握MATLAB/SIMULINK在无线通信系统仿真中的应用;③评估不同调制方式、信道估计算法同步算法的优劣;④为实际OFDM系统的设计优化提供理论依据技术支持。; 其他说明:本文不仅提供了详细的理论分析,还附带了大量的MATLAB代码示例,便于读者动手实践。建议读者在学习过程中结合代码进行调试实验,以加深对OFDM技术的理解。此外,文中还涉及了一些最新的研究方向技术趋势,如AI增强毫米波通信,为读者提供了更广阔的视野。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值