乐高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.

相关热词搜索:编程 代理 方法 代理及其编程方法 编程通讯线代理 可编程触摸屏代理

版权所有 蒲公英文摘 www.zhaoqt.net