乐高ev3机器人编程自学 [代理及其编程方法]
发布时间:2020-02-16 来源: 散文精选 点击:
摘要:在C#语言中,可以用代理(Delegate)来动态地调用不同的函数。本文针对代理的基本概念、用代理编程的基本结构及多重代理进行讨论,并通过实例程序进行必要的说明。
关键词:函数指针;代理;多重代理
中图分类号:TP312 文献标识码:A
1 代理的基本概念
在C/C++中,函数调用有两种方式:一种是通过函数名直接调用;另一种是利用指向函数的指针进行调用。指向函数的指针又简称为函数指针。采用函数指针调用函数,可以达到通过一个函数指针调用多个功能不同、但函数的参数及返值类型一致的函数。
1.1 函数指针
在C/C++中,函数名表示函数在存储区域的首地址,即是函数执行的入口地址。在程序中调用一个函数时,程序控制流程将转移到以该函数名为入口地址的地方执行该函数。
函数指针定义的一般形式是:
数据类型(*函数指针名)(参数表);
其中数据类型是函数指针所指向的函数所具有的返值类型,参数表是被调函数所具有的参数表。
一般来讲,函数指针得到某个函数名的赋值,程序将调用该函数;在函数指针作为函数的形参时,需要进行取内容的运算才能调用对应的函数。例如:
exe(int x,int y,int (*func)())
{ return((*func)(x,y));}
在上述exe函数中,int (*func)()是函数指针,而“((*func)(x,y));”程序将转向由func所指向的函数去执行,其参数是x和y。
使用函数指针编程方法一般为:定义相应的函数;定义函数指针;通过某种操作使函数指针得到函数名的赋值;最后连同参数调用相应的函数。
1.2 代理及其相关概念
在C#中,代理(delegate)的作用类似于C/C++中的函数指针。C#中的方法类似于C/C++中的函数,其方法名也是一个物理地址。方法的入口地址可以赋给“代理”,进而通过“代理”调用该方法;且同一个代理可以调用多个不同的方法。使用代理可以在程序的运行期间动态地调用所需的函数。
代理定义的一般格式为:
[访问控制修饰符] delegate返值类型 代理名([形式参数表]);
其中:访问控制修饰符可以是new、public、protected、internal和private。new修饰符表明当前定义的代理将隐藏继承来的同名代理;public修饰符表明所有对象都可以访问该定义的代理;protected修饰符表明定义该代理的类及其子类可以访问该代理;internal修饰符只有代理所属的工程项目的成员可以访问该代理;private修饰符表明只有定义该代理的类才能访问该代理。
delegate是定义代理的关键字。代理名是符合C#的任意合法的标识符。返值类型是指该代理所调用方法的返值类型。形式参数表用于指出代理所调用方法的参数表。
例如,在一个类中定义一个代理MyDel_ egate:
classMyClass
{ ……
public delegate void MyDelegate(string s);
……
}
在上述代理定义中的void表示该代理指向的方法不返回任何值,string s表示该代理所指向的方法将接受一个字符串参数。
在C#中,定义一个代理MyDelegate, C#编译器将根据代理的定义语句自动生成一个从System.MulticastDelegate类派生的子类MyDelegate。
2 使用代理的编程结构
代理类似于C/C++的指向函数的指针,但是代理的使用范围比函数指针更加广泛。在C中,函数只有外部和静态函数两种,它们都属于静态函数的范畴;在C ++中,函数指针只能引用静态方法,而代理不仅可以引用静态方法,还可以引用对象的实例方法。
2.1 使用代理的编程结构
使用代理编程和在C/C++中使用函数指针类似,一般有:定义将要由代理调用的方法;声明一个代理;定义delegate处理的函数;创建实例并调用相应方法。
(1)定义拟调用的方法
该类方法的函数返值类型,函数形参的参数类型、个数及参数的顺序,决定了定义delegate类型代理时的相应参数。例如:
public static string FunctionA
(string name) {……}
public static string FunctionB
(string name) {……}
(2)声明一个代理
该代理的返值类型,参数的类型、个数及参数的顺序都必须与拟调用的方法的返值类型,形参类型、个数及参数的顺序完全相同。例如:
public delegate string MyDelegate (stringname);
(3)定义delegate类型处理的函数
定义delegate类型处理的函数,并在此函数中通过delegate类型调用定义的方法。
public static void MethodA(MyDelegateMe)
{Console.WriteLine(Me(“张三”));}
(4)创建实例并调用方法
由于声明一个delegate类型的代理在编译时将被转换成一个MulticastDelegate类的派生类,因此在使用代理时,必须要先创建该类的实例,并把它与一个方法关联。
MyDelegate a=new MyDelegate (FunctionA)
本语句的含义是:a引用指向方法FunctionA的程序代码段。
通过delegate处理函数调用相应的方法。MethodA(a);
2.2 使用代理编程实例
using System;
namespace ConDelegatejl
{ public class Test
{ //第一步:声明委托
public static string FunctionA(string name)
{return“A say Hello to”+name;}
public static string FunctionB(string name)
{return “B say Hello to ”+name;}
//第二步:定义被调用的方法
public delegate string MyDele gate(string name);
//第三步:定义delegate类型处理函数
public static void MethodA (MyDelegate Me)
{Console.WriteLine(Me(“张三”));}
public static void Main()
{ //第四步:创建实例,
//准备调用的方法名
MyDelegate a = new MyDelegate (FunctionA);
MyDelegate b = new MyDelegate (FunctionB);
MethodA(a); MethodA(b); Console.Read();
}
}
上述程序的运行结果如下图所示。
3 多重代理及其实现
在C#语言中,每一个代理实例(对象)都含有一个调用链表,该链表可以包含多个该代理要调用的方法,此种机制用于一个代理实例可以调用多个方法,也就是多重代理(多播)。多播要创建方法链表,当调用代理时,所有被链接的方法都会被自动地调用。
3.1 Delegate类和MulticastDelegate类
所有的代理类都是由MulticastDele gate类派生的,而MulticastDelegate类又是由Delegate类派生的,它们都位于System命名空间下。
Delegate类有两个公用的只读属性:
Method属性:本属性用于获得代理实例要调用的静态方法。可以用如下代码获得代理对象所调用的方法名。
stringMethodName=代理名.Method;
Target属性:该属性可以获得代理对象所在的类;如果代理调用的是静态方法,其返值为null,否则将返回代理实例所调用方法所在类名。如下代码可以获得代理调用方法所在类的名称。
stringObjType=代理名.Target;
如果代理调用多个方法,则该属性将返回调用列表中最后一个实例方法所在的类名。
3.2 MulticastDelegate类实现多重代理的机制
从MulticastDelegate类派生的用户代理实例中含有一个调用链表,该链表将由多个代理实例组成,其中每个代理实例都封装一个相应的方法,也就是一个代理实例可以同时调用多个方法。代理实例通过_prev私有指针来连接多个代理构成的链表,私有指针_target和_methodPtr用于指向代理实例调用的实例和方法。
每创建一个新的代理实例时,指针_prev将被设置为null,表示链表中没有其它的代理实例。而当用户使用Combine方法(或+=运算符)把另一个代理实例合并到该调用链表中时,则将先创建一个含有_target和_methodPtr值的新实例,然后把该实例的_prev设置为调用链表的头实例,即从调用链表的头插入新的实例。可以使用Remove方法(或-=运算符)从一个调用链表中删除一个代理实例。
3.3 多重代理编程的实现
多重代理(或多播)具有创建方法链表的能力,当调用代理时,所有被链接的方法都会被自动调用,也就是多播可以在一次代理调用中调用方法链表上的所有方法。创建多播调用链表的方法是:先实例化一个代理,然后使用“+=”运算符把方法添加到调用链表中;也可以使用“-=”运算符从调用链表中删除一个方法。
下面程序说明了多重代理编程的实现方法。
using System;
using System.Collections.Generic;
using System.Text;namespace ConDelegatej2
{
delegate void StringDelegate(
ref string str);//定义一个代理
class stringops//字符串操作类
{ //定义三个字符串操作方法
static void ReplaceSpaces(ref string s)
{ Console.WriteLine("用连字符替换
空格操作:");
s = s.Replace(" ", "_");
}
static void RemoveSpaces(ref string s)
{ string temp = "";int i;
Console.WriteLine("删除空格操作:");
for(i=0; i=0;j--)
temp += s[j];
s = temp;
}
static void Main(string[]args)
{//创建一个代理
StringDelegate strdelegate;
StringDelegate replacesp=new
StringDelegate(ReplaceSpaces);
StringDelegate removes =new
StringDelegate(RemoveSpaces);
StringDelegate reversestr=new StringDelegate(Reverse);
string str = "Iam astudent.";
//代理指向一个函数
strdelegate =replacesp;
strdelegate += reversestr;//多播关联
strdelegate(ref str );//调用多播
Console.WriteLine("操作字符串的结果为:"+str);
Console.WriteLine();
strdelegate-=replacesp;//去除replacesp
strdelegate += RemoveSpaces;
str ="你是一个计算机教师.";
strdelegate (refstr);
Console.WriteLine("字符串操作结果是:"+str);
Console.WriteLine ();Console.Read();
}
}
}
上述程序的运行结果如下所示。
4 结束语
代理是C#语言提供动态调用函数的一种方法;多重代理解决了用一个代理一次调用多个方法的技术。不过代理的多播有一个限制:方法链表中的方法必须具有相同的参数,而且这些方法的返回值类型要是void类型。
代理也为用户程序利用属性驱动事件程序的编程提供了有力的方法。
参考文献
[1]唐大仕编著. C#程序设计教程[M]. 北京: 清华大学出版社; 北方交通大学出版社, 2003.
[2]邵鹏鸣编著. Visual C#程序设计基础教程[M]. 北京: 清华大学出版社, 2005.
相关热词搜索:编程 代理 方法 代理及其编程方法 编程通讯线代理 可编程触摸屏代理
热点文章阅读