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)); } |
d(10) result = 100 d(10) result = 1000 |
using namespace System; ref struct StrCompare { static int CompareExact(String^ s1, String^ s2) { Console::WriteLine("Comparing {0} and {1} ""using CompareExact", s1, s2); // ... return 0; } static int CompareIgnoreCase(String^ s1, String^ s2) { Console::WriteLine("Comparing {0} and {1}" "using CompareIgnoreCase", s1, s2); // ... return 0; } }; delegate int Compare(String^ s1, String^ s2); /*1*/ Compare^ FindComparisonMethod() { // ... } void Sort(Compare^ compare) { int result; /*3*/ result = compare("Hello", "Hello"); /*4*/ result = compare("Hello", "HELLO"); /*5*/ result = compare("Hello", "Hell"); } int main() { /*6*/ Sort(gcnew Compare(&StrCompare::CompareIgnoreCase)); /*7*/ Sort(FindComparisonMethod()); /*8*/ FindComparisonMethod()("Red", "RED"); } |
Compare代理类型可对任意接受两个String^参数并返回一个int结果的函数进行包装,在此,有两个函数为StrCompare::CompareExact和StrCompare::CompareIgnoreCase。
在标号6中,创建了一个Compare代理类型的实例,用它来包装StrCompare::CompareIgnoreCase,并把此代理句柄传递给Sort函数,其将会利用比较函数进一步进行处理。
正如大家所看到的,Sort可接受一个代理类型的参数--而此参数可像其他函数参数一样,可为传值、传址、传引用。
在标号7中,调用了FindComparisonMethod函数,其返回一个Del代理类型,接着在标号7及8中调用了包装过的函数。此处要重点说一下标号8:首先,FindComparisonMethod函数是被调用来获取代理实例--其常用于调用底层函数;其次,这两个函数的调用操作符都有同等的优先级,所以它们从左至右调用。#p#分页标题#e#
FindComparisonMethod函数中也用了一些逻辑用于确定到底需要包装哪个函数,此处就未作详细说明了。
在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)); } |
d(10) result = 100 d(10) result = 1000 |
using namespace System; ref struct StrCompare { static int CompareExact(String^ s1, String^ s2) { Console::WriteLine("Comparing {0} and {1} ""using CompareExact", s1, s2); // ... return 0; } static int CompareIgnoreCase(String^ s1, String^ s2) { Console::WriteLine("Comparing {0} and {1}" "using CompareIgnoreCase", s1, s2); // ... return 0; } }; delegate int Compare(String^ s1, String^ s2); /*1*/ Compare^ FindComparisonMethod() { // ... } void Sort(Compare^ compare) { int result; /*3*/ result = compare("Hello", "Hello"); /*4*/ result = compare("Hello", "HELLO"); /*5*/ result = compare("Hello", "Hell"); } int main() { /*6*/ Sort(gcnew Compare(&StrCompare::CompareIgnoreCase)); /*7*/ Sort(FindComparisonMethod()); /*8*/ FindComparisonMethod()("Red", "RED"); } |
Compare代理类型可对任意接受两个String^参数并返回一个int结果的函数进行包装,在此,有两个函数为StrCompare::CompareExact和StrCompare::CompareIgnoreCase。
在标号6中,创建了一个Compare代理类型的实例,用它来包装StrCompare::CompareIgnoreCase,并把此代理句柄传递给Sort函数,其将会利用比较函数进一步进行处理。
#p#分页标题#e#
正如大家所看到的,Sort可接受一个代理类型的参数--而此参数可像其他函数参数一样,可为传值、传址、传引用。
在标号7中,调用了FindComparisonMethod函数,其返回一个Del代理类型,接着在标号7及8中调用了包装过的函数。此处要重点说一下标号8:首先,FindComparisonMethod函数是被调用来获取代理实例--其常用于调用底层函数;其次,这两个函数的调用操作符都有同等的优先级,所以它们从左至右调用。
FindComparisonMethod函数中也用了一些逻辑用于确定到底需要包装哪个函数,此处就未作详细说明了。
System::Delegate
代理类型的定义,会隐式地创建一个对应的类(class)类型,并且所有的代理类型均从类库System::Delegate继承而来。要定义一个这样的类,唯一的方法就是通过delegate上下文关键字。代理类为隐式的sealed,因此它们不能被用作基类。另外,一个非代理类也不能从System::Delegate继承。
例6演示了几个Delegate函数的用法:
例6:
using namespace System; delegate void D(int x); ref class Test { String^ objName; public: Test(String^ objName) { this->objName = objName; } void M(int i) { Console::WriteLine("Object {0}: {1}", objName, i); } }; void ProcessList(D^ del, int value, Object^ objToExclude); int main() { /*1*/ Test^ t1 = gcnew Test("t1"); D^ cd1 = gcnew D(t1, &Test::M); /*2*/ Test^ t2 = gcnew Test("t2"); D^ cd2 = gcnew D(t2, &Test::M); /*3*/ Test^ t3 = gcnew Test("t3"); D^ cd3 = gcnew D(t3, &Test::M); /*4*/ D^ list = cd1 + cd2 + cd3 + cd2; /*5a*/ ProcessList(list, 100, nullptr); /*5b*/ ProcessList(list, 200, t1); /*5c*/ ProcessList(list, 300, t2); /*6a*/ D^ cd4 = cd1 + cd2; /*6b*/ D^ cd5 = (D^)cd4->Clone(); /*6c*/ ProcessList(cd4, 5, nullptr); /*6d*/ ProcessList(cd5, 6, nullptr); } void ProcessList(D^ del, int value, Object^ objToExclude) { /*7*/ if (del == nullptr) { return; } /*8*/ else if (objToExclude == nullptr) { del(value); } else { /*9*/ array<Delegate^>^ delegateList = del->GetInvocationList(); for each (Delegate^ d in delegateList) { /*10*/ if (d->Target != objToExclude) { /*11*/ ((D^)d)(value); } } } } |
Object t1: 100 Object t2: 100 Object t3: 100 Object t2: 100 Object t2: 200 Object t3: 200 Object t2: 200 Object t1: 300 Object t3: 300 |
Object t1: 5 Object t2: 5 Object t1: 6 Object t2: 6 |
关于函数ProcessList,如果参数中的代理实例为nullptr,即没有调用列表,那它将直接返回;如果排除的对象为nullptr,那么列表中所有的函数都将被调用;如果存在要排除的对象,就要像标号8中那样把调用列表当作代理数组取出,接着,在标号9中逐个排查不相符的入口,最后,在标号10中调用余下的这些函数。尽管在调用列表中每个入口都是Del类型,但GetInvocationList返回一个基类Delegate数组,所以在调用每个代理实例之前,需像标号10那样先转换成类型D。
事件
在C++/CLI中,事件是一种当某种重要事情发生时,为客户程序提供通知的机制。鼠标单击就是事件的一个典型例子,在事件发生之前,有关的客户程序必须先注册它们感兴趣的事件,如,当检测到鼠标单击时,这些程序就会接到通知。
通过添加或删除一个或多个感兴趣的事件,事件列表可在运行时增长或缩减,请看例7中Server类型的定义,在标号1中,Server类定义了代理类型NewMsgEventHandler(一般约定在用于事件处理时,代理类型添加EventHandler的后缀名),接着,在标号2中,定义了一个名为ProcessNewMsg的公共事件(event在此为一个上下文关键字)。一个事件必须有一个代理类型,实际上,像这样的一个事件已经是一个代理实例了,而且因为它被默认初始化为nullptr,所以它没有调用列表。#p#分页标题#e#
例7:
using namespace System; public ref struct Server { /*1*/ delegate void NewMsgEventHandler(String^ msg); /*2*/ static event NewMsgEventHandler^ ProcessNewMsg; /*3*/ static void Broadcast(String^ msg) { if (ProcessNewMsg != nullptr) { ProcessNewMsg(msg); } } }; |
using namespace System; public ref class Client { String^ clientName; /*4*/ void ProcessNewMsg(String^ msg) { Console::WriteLine("Client {0} received message {1}", clientName, msg); } public: Client(String^ clientName) { this->clientName = clientName; /*5*/ Server::ProcessNewMsg += gcnew Server::NewMsgEventHandler(this, &Client::ProcessNewMsg); } /*6*/ ~Client() { Server::ProcessNewMsg -= gcnew Server::NewMsgEventHandler(this, &Client::ProcessNewMsg); } }; |
using namespace System; int main() { Server::Broadcast("Message 1"); Client^ c1 = gcnew Client("A"); Server::Broadcast("Message 2"); Client^ c2 = gcnew Client("B"); Server::Broadcast("Message 3"); Client^ c3 = gcnew Client("C"); Server::Broadcast("Message 4"); c1->~Client(); Server::Broadcast("Message 5"); c2->~Client(); Server::Broadcast("Message 6"); c3->~Client(); Server::Broadcast("Message 7"); } |
Client A received message Message 2 Client A received message Message 3 Client B received message Message 3 Client A received message Message 4 Client B received message Message 4 Client C received message Message 4 Client B received message Message 5 Client C received message Message 5 Client C received message Message 6 |
public ref struct Server { // ... static event NewMsgEventHandler^ ProcessNewMsg { void add(NewMsgEventHandler^ n) { /* ... */ } void remove(NewMsgEventHandler^ n) { /* ... */ } } // ... }; |
评论 {{userinfo.comments}}
{{child.content}}
{{question.question}}
提交