实例解析C++/CLI之代理与事件

在C++/CLI中,代理是对函数进行包装的对象;而事件是一种为客户程序提供通知的类机制。

在前几篇文章中,已经多次演示了如果让一个句柄在不同的时间,被引用至不同的对象,从而以更抽象的方法来解决程序中的问题,但是,也能使用代理通过函数来达到同样的效果;代理是包装了函数的一个对象,且对实例函数而言,也能通过特定的实例,与这些函数发生联系。一旦一个代理包装了一个或多个函数,你就能通过代理来调用这些函数,而无须事先了解包装了哪些函数。

请看例1中的代码,在标号1中,定义一个代理类型Del,由于使用了上下文关键字delegate,所以有点像函数的声明,但与函数声明不同的是,此处声明的是一个代理类型Del的实例,其可包装进任意接受一个int类型作为参数并返回一个int值类型的函数(任意有效的参数列表及返回类型组合都是允许的)。一旦定义了某种代理类型,它只能被用于包装具有同样类型的函数;代理类型可被定义在源文件中或命名空间的范围内,也能定义在类中,并可有public或private访问控制属性。

例1:

using namespace System;
ref struct A
{
  static int Square(int i)
  {
   return i * i;
  }
};
ref struct B
{
  int Cube(int i)
  {
   return i * i * i;
  }
};
/*1*/
delegate int Del(int value);
int main()
{
  /*2*/ Del^ d = gcnew Del(&A::Square);
  /*3*/ Console::WriteLine("d(10) result = {0}", d(10));
  /*4*/ B^ b = gcnew B;
  /*5*/ d = gcnew Del(b, &B::Cube);
  /*6*/ Console::WriteLine("d(10) result = {0}", d(10));
}

静态函数A::Square与实例函数B::Cube对Del来说,都具有相同的参数类型及返回类型,因此它们能被包装进同类型的代理中。注意,即使两个函数均为public,当考虑它们与Del的兼容性时,它们的可访问性也是不相关的,这样的函数也能被定义在相同或不同的类中,主要由程序员来选择。

一旦定义了某种代理类型,就可创建此类型实例的句柄,并进行初始化或赋值操作,如标号2中所示的静态函数A::Square,及标号5中所示的实例函数B::Cube。(此处只是出于演示的目的,否则把Cube做成实例函数没有任何好处。)

创建一个代理实例涉及到调用一个构造函数,如果是在包装一个静态函数,只需传递进一个指向成员函数的指针;而对实例函数而言,必须传递两个参数:一个实例的句柄及指向实例成员函数的指针。

在初始化代理实例之后,就能间接地调用它们包装的函数了,用法与直接调用原函数一样,只不过现在用的是代理实例名,如标号3与6,由包装函数返回的值也是像直接调用函数时那样获得。如果一个代理实例的值为nullptr,此时再试图调用被包装的函数,会导致System::NullReferenceException类型异常。

以下是输出:

d(10) result = 100
d(10) result = 1000

传递与返回代理

有时,把包装好的函数传递给另一个函数,会非常有用,接受一方的函数并不知道会传递过来哪个函数,并且它也无须关心,只需简单地通过包装好的代理,间接调用此函数就行了。

下面以集合中元素排序来说明,大多数时候,集合中元素排序所依据的规则,只在对某对元素进行比较的方法上存在区别。如果在运行时提供进行比较的函数,一个排序过程就能用相应定义的比较函数排出任意的顺序,请看例2。

时间: 2024-11-02 07:37:18

实例解析C++/CLI之代理与事件的相关文章

实例解析C++/CLI之静态构造函数

就某些类而言,当在程序中第一次使用时,最好能有一个初始化过程:当程序不再需要时,也最好能做一些收尾工作,这些都是非常好的类设计习惯. 引出问题 如果有这样一种情况,某种类型的每个实例都必须有其唯一的ID,比如说某种交易类型,这些ID可用于在处理过程中追踪每笔交易,或之后用于审计员查看数据文件:为讨论方便,此处的ID为从0起始的有符号整型数. 如果把一个nextID值保存在内存中,并在每个新实例构造时,把它递增1,这无疑是一个不错的想法,但是,为使在程序连续的执行过程中保持ID值的唯一,就需要在每

实例解析C++/CLI之开卷有益

C++/CLI可以说是标准C++语言一种新的"方言",它是Microsoft为充分利用CLI(Common Language Infrastructure)平台而开发出来的.那么,它在语言方面有何新颖独到之处呢,下面,就让我们一起开始奇妙的C++/CLI语言之旅(文中所有示例代码,均以Visual Studio.NET 2005 Professional编译通过,所有的讲解内容,也均以Visual Studio.NET 2005环境为基础). 程序集与元数据 传统的C++编译模式包括把

实例解析C++/CLI之值类型

值类型是一种轻量级的C++/CLI类机制,非常适合于小型的数据结构,且从语义的角度来看,与数值(Value)类似. 与之相比,引用类型的实例--包括那些声明在堆栈上的,是由垃圾回收器管理的,而值类型的实例却不是.一般来说,一个值类较好的实现应只有一些数据成员,而不需要继承性,这样,在函数传递及返回值.或是赋值操作时,不会带来巨大的数据开销. 值类初印像 请看例1中的Point类,可以通过替换ref为value,来把一个引用类变为值类:与引用类(ref)相似,值类(value)也是一个包含了空格的

实例解析C++/CLI中的接口与泛型

接口 某些时候,让不相关的类分享一组公有成员,以便产生相同的行为,是非常有用的.一个最基本的方法可能是通过一个公共的基类来定义它们,但这种方法太受局限,因为它要求这些类通过继承而互相关联,另外,它们也许还有着各自的基类,且CLI类型只支持单一类继承. C++/CLI提供了一种方法,可利用多个类实现一组通用的功能,这就是我们通称的"接口",而一个接口则是一组成员函数的声明.要注意,这些函数只是声明,没有定义,也就是说,一个接口定义了一个由抽象函数组成的类型--这些函数实际上是纯虚函数,且

实例解析C++/CLI中的继承与枚举

本文中,将要介绍与继承相关的C++/CLI主题,并以现实生活中银行交易的三种形式:存款.取款.转账,来说明类的继承体系,且以一种新的枚举形式来实现. 枚举器 请看例1中声明的类型,它存在于其自身的源文件中,并编译为一个只包含此类型的程序集: 例1: public enum class TransactionType : unsigned char {Deposit, Withdrawal, Transfer}; 与想像的一样,枚举器中的Deposit.Withdrawal.Transfer分别代

实例解析C++/CLI的输入与输出

当使用标准C++编程时,我们已开始接触到两个主要的I/O"工具":标准C头文件cstdio和标准C++中与流相关的头文件iostream,如果加上Windows的话,那么还有Win32库和MFC库,另外,还有CLI/.NET.本文将要探讨的,就是C++/CLI中的输入与输出. 简介 日常,我们与文件或设备进行通讯的逻辑通道,称为流.数据可以8位字节或16位Unicode字符形式进行读写,而两者都有其自己的类集:另外,还有用于在字节与字符之间转换的类.其中,字符流通过Stream类及其的

实例解析C++/CLI线程之多任务

简介 从处理器的角度来看,线程是一个单独的执行流程,每个线程都有各自的寄存器及堆栈上下文.通常来说,在系统中只有一个处理器或处理器只有一个核心时,运行时环境在一个时间片内只能执行一个线程,当线程未能获取所需的资源时,线程的执行就会被中断,且会一直等到相关操作的完成,如I/O:或者在线程用完它的处理器时间片时,也会被中断下来等待.而处理器把执行流程从一个线程切换到另一个线程时,这称为"上下文切换":当某个线程变为"阻塞"状态,从而执行另一个线程时,系统有效地减少了处理

实例解析C++/CLI的“克隆”

C++/CLI不但支持基于堆栈的对象,同时也支持基于堆的对象:然而,如果想与其他基于CLI的语言(如C#.J#.Visual Basic)进行互操作的话,必须要清楚地知道,这些语言只支持基于堆的对象:当处于基于堆的对象环境中时,你与对象之间,永远只有"一臂之遥",比方说,两个给定的句柄h1与h2,只有在为这种句柄类型定义了相应的赋值操作符时,*h1 = *h2才会工作正常,而对C++/CLI之外的其他语言中的类型来说,情况可能就不是这样了.同样地,一个遵从CLS的机制需要创建对象的一份

实例解析C++/CLI线程之线程状态持久性

其他形式的同步 我们可使用类Monitor与类Thread中的某些函数,直接控制线程的同步,请看例1. 例1: using namespace System; using namespace System::Threading; int main() { /*1*/ MessageBuffer^ m = gcnew MessageBuffer; /*2a*/ ProcessMessages^ pm = gcnew ProcessMessages(m); /*2b*/ Thread^ pmt =