C# 事件 vs 委托

我们在以前的文章中看到了委托以及它们的实现。但是如果你在网页上搜索有关委托的信息,你肯定会注意到它们总是与“event”结构相关联。
联机事件教程使得事件尽管与常规的委托实例有关系,但是还是有许多区别。事件经常被解释得就好像它们是一种特殊的类型或结构。但是我们将看到它们只是委托类型的一种修饰器,它们仅仅是添加了一些编译器强制执行的限制和两个存取器(与属性的get和set相似)。

首先看看事件 vs 常规委托
当我完成了前一篇关于委托的文章时,另一个C#构造也进入了我的计划:事件。事件看起来确实与委托有关,我没能找出它们之间的不同。

从它们的语法看来,事件就好像是一个留有代表多播委托的委托组合字段。它们同样支持委托的(+和-)组合操作。
在接下来的例子程序(没有任何有用的功能)中,我们将看见msgNotifier(使用event结构)和msgNotifier2(普通委托)看起来有个一致的意图和目的。

代码
namespace EventAndDelegate
{
  delegate void MsgHandler(string s);

  class Class1
  {
   public static event MsgHandler msgNotifier;
   public static MsgHandler msgNotifier2;
   [STAThread]
   static void Main(string[] args)
   {
    Class1.msgNotifier += new MsgHandler(PipeNull);
    Class1.msgNotifier2 += new MsgHandler(PipeNull);
    Class1.msgNotifier("test");
    Class1.msgNotifier2("test2");
   }
 
   static void PipeNull(string s)
   {
    return;
   }
  }
}
查看在上述代码中Main方法的IL代码,你会注意到msgNotifier和msgNotifier2都是委托,msgNotifier2使用的是同样的方式。


代码
.method private hidebysig static void Main(string[] args) cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // Code size 95 (0x5f)
  .maxstack 4
  IL_0000: ldsfld class EventAndDelegate.MsgHandler  EventAndDelegate.Class1::msgNotifier
  IL_0005: ldnull
  IL_0006: ldftn void EventAndDelegate.Class1::PipeNull(string)
  IL_000c: newobj instance void EventAndDelegate.MsgHandler::.ctor(object,
     native int)
  IL_0011: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
     class [mscorlib]System.Delegate)
  IL_0016: castclass EventAndDelegate.MsgHandler
  IL_001b: stsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier
  IL_0020: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
  IL_0025: ldnull
  IL_0026: ldftn void EventAndDelegate.Class1::PipeNull(string)
  IL_002c: newobj instance void EventAndDelegate.MsgHandler::.ctor(object,
     native int)
  IL_0031: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
     class [mscorlib]System.Delegate)
  IL_0036: castclass EventAndDelegate.MsgHandler
  IL_003b: stsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
  IL_0040: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier
  IL_0045: ldstr "test"
  IL_004a: callvirt instance void EventAndDelegate.MsgHandler::Invoke(string)
  IL_004f: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
  IL_0054: ldstr "test2"
  IL_0059: callvirt instance void EventAndDelegate.MsgHandler::Invoke(string)
  IL_005e: ret
} // end of method Class1::Main
查看一下在MSDN上的C#关键字,它证明了event仅仅是一个修饰符。问题是这样使用后会带来什么方面的不同呢?


 

事件增加的值
事件与接口
首先,一个事件可以包含在接口声明中,而一个字段(译注:意指普通委托)不能。这是引入event修饰符后最重要的行为改变。例如:

代码
interface ITest
{
  event MsgHandler msgNotifier; // compiles
  MsgHandler msgNotifier2; // error CS0525: Interfaces cannot contain fields
}
 
class TestClass : ITest
{
  public event MsgHandler msgNotifier; // When you implement the interface, you need to implement the event too
  static void Main(string[] args) {}
}

 

事件引用
更多的是,一个事件仅能被包含其声明的类调用,然而委托字段可以任何有权限访问它的人调用。例如:


代码
using System;

namespace EventAndDelegate
{
  delegate void MsgHandler(string s);

  class Class1
  {
   public static event MsgHandler msgNotifier;
   public static MsgHandler msgNotifier2;

   static void Main(string[] args)
   {
    new Class2().test();
   }
  }
 
  class Class2
  {
   public void test()
   {
    Class1.msgNotifier("test"); // error CS0070: The event 'EventAndDelegate.Class1.msgNotifier' can only appear on the left hand side of += or -= (except when used from within the type 'EventAndDelegate.Class1')
    Class1.msgNotifier2("test2"); // compiles fine
   }
  }
}
在引用上这个限制是非常强的。甚至从声明事件的父类继承的派生类也不被允许触发事件。处理这种事情的一个方法是定义一个protected virtual方法来触发事件。


 

事件存取器
同时,事件将伴随着一对存取方法,它们有一个add和remove方法。这与属性非常相似,属性也提供了一对get和set方法。

你被允许重载引用自MSDN的关于C#事件修饰符的例2和例3中显示的那些存取器,尽管我没有看到例2有什么用处,但是你可以假设你能够针对某些通知编写自定义的添加方法或写入日志,例如,当一个监听者加入到你的事件中。

add和remove存取器需要同时自定义,否则将产生CS0065错误('Event.TestClass.msgNotifier' : 事件属性必须同时包含add和remove存取器)。
查看前一个例子的IL代码,里面的事件存取器并没有自定义,我注意到编译器自动生成的针对msgNotifier事件的方法(add_msgNotifier和remove_msgNotifier)。但是它们并没有使用,无论何时事件被访问,相同的IL代码将会被复制(内联方式)。
但是当你自定义这些存取器后再查看IL代码时,你将注意到自动生成的存取器当你访问事件的时候使用了。例如,代码如下:

代码
using System;

namespace Event
{
  public delegate void MsgHandler(string msg);

  interface ITest
  {
   event MsgHandler msgNotifier; // compiles
   MsgHandler msgNotifier2; // error CS0525: Interfaces cannot contain fields
  }
 
  class TestClass : ITest
  {
   public event MsgHandler msgNotifier
   {
    add
    {
     Console.WriteLine("hello");
     msgNotifier += value;
    }

   }
 
   static void Main(string[] args)
   {
    new TestClass().msgNotifier += new MsgHandler(TestDel);
   }
   static void TestDel(string x)
   {
   }
  }
}
下面的是针对Main方法的IL代码:


代码
{
  .entrypoint
  // Code size 23 (0x17)
  .maxstack 4
  IL_0000: newobj instance void Event.TestClass::.ctor()
  IL_0005: ldnull
  IL_0006: ldftn void Event.TestClass::TestDel(string)
  IL_000c: newobj instance void Event.MsgHandler::.ctor(object,
     native int)
  IL_0011: call instance void Event.TestClass::add_msgNotifier(class Event.MsgHandler)
  IL_0016: ret
} // end of method TestClass::Main

 

事件签名
最后,尽管C#允许,.net框架增加一个被用于事件的关于委托签名的限制。这个签名应为foo(object source, EventArgs e),这里的source表示触发事件的对象,e包含一些关于事件的附加信息。

 

结论
我们已经看到event关键字是一个针对委托声明的修饰符,它允许它被包含在一个接口,限制它从声明它的类中引用,提供一对可定义的存取器(add和remove)并且强制委托签名(当在.net框架中使用时)。

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/12639172/viewspace-623971/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/12639172/viewspace-623971/

本课题设计了一种利用Matlab平台开发的植物叶片健康状态识别方案,重点融合了色彩与纹理双重特征以实现对叶片病害的自动化判别。该系统构建了直观的图形操作界面,便于用户提交叶片影像并快速获得分析结论。Matlab作为具备高效数值计算与数据处理能力的工具,在图像分析与模式分类领域应用广泛,本项目正是借助其功能解决农业病害监测的实际问题。 在色彩特征分析方面,叶片影像的颜色分布常与其生理状态密切相关。通常,健康的叶片呈现绿色,而出现黄化、褐变等异常色彩往往指示病害或虫害的发生。Matlab提供了一系列图像处理函数,例如可通过色彩空间转换与直方图统计来量化颜色属性。通过计算各颜色通道的统计参数(如均值、标准差及主成分等),能够提取具有判别力的色彩特征,从而为不同病害类别的区分提供依据。 纹理特征则用于描述叶片表面的微观结构与形态变化,如病斑、皱缩或裂纹等。Matlab中的灰度共生矩阵计算函数可用于提取对比度、均匀性、相关性等纹理指标。此外,局部二值模式与Gabor滤波等方法也能从多尺度刻画纹理细节,进一步增强病害识别的鲁棒性。 系统的人机交互界面基于Matlab的图形用户界面开发环境实现。用户可通过该界面上传待检图像,系统将自动执行图像预处理、特征抽取与分类判断。采用的分类模型包括支持向量机、决策树等机器学习方法,通过对已标注样本的训练,模型能够依据新图像的特征向量预测其所属的病害类别。 此类课题设计有助于深化对Matlab编程、图像处理技术与模式识别原理的理解。通过完整实现从特征提取到分类决策的流程,学生能够将理论知识与实际应用相结合,提升解决复杂工程问题的能力。总体而言,该叶片病害检测系统涵盖了图像分析、特征融合、分类算法及界面开发等多个技术环节,为学习与掌握基于Matlab的智能检测技术提供了综合性实践案例。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值