C#委托详解

来源:互联网 发布:mastercam铣螺纹编程 编辑:程序博客网 时间:2024/05/21 09:07
C#中的委托类似C、C++中的函数指针。使用委托,程序员可以将方法引用封装在委托对象类。然后可以将该委托对象传递给可调用所引用方法的代码,而不必再编译时知道将调用哪个方法。委托时一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托时定义回调方法的理想选择。
与c和c++的函数指针不同,委托时面向对象、类型安全的。

1、委托的声明

两个步骤:
    首先定义要使用的委托,即告诉编译器这种类型的委托代表了哪种类型的方法。
    然后创建该委托的一个或多个实例。
    其定义语法为:  delegate void IntMethodInvoker(int a);
    上面这句话表明,定义了一个委托IntMethodInvoker,并指定该委托的每个实例都包含一个方法细节,该方法带有一个int参数,并且返回值为void

    实际上定义一个委托就是定义一个”新类“。它可以定义在任何地方。

2、委托的使用

    例子如下:
  
  private delegate string GetString();//此处定义一个没有参数,且返回值为string的委托        static void Main(string[] args)        {            int x = 23;            //GetString firstString = new GetString(x.ToString);            //上一句也等价于下一句,但是注意不能写成GetString firstString = x.ToString();            GetString firstString = x.ToString;//这为委托推断            Console.WriteLine("String is {0}", firstString());            //上一句中firstString()等价于x.ToString()方法            Console.ReadLine();        }

注意:不能调用x.ToString()方法把它传递给委托变量,因为调用该方法返回的事一个能赋予委托变量的字符串对象,只能该方法的地址赋予委托变量。

3、委托示例

class test    {        public static double MultiplyByTwo(double value)        {            return value * 2;        }        public static double Square(double value)        {            return value * value;        }    }    delegate double DoubleOperation(double x);    class Program    {        static void Main(string[] args)        {            DoubleOperation[] operations = { test.MultiplyByTwo, test.Square };            for (int i = 0; i < operations.Length;i++ )            {                Console.WriteLine("Using operations[{0}]:", i);                display(operations[i], 2.0);                display(operations[i], 7.94);                display(operations[i], 1.233);                Console.WriteLine();            }            Console.ReadLine();        }        static void display(DoubleOperation action, double value)        {            double result = action(value);            Console.WriteLine("Value is {0},result of operation is{1}", value, result);        }    }


输出结果为:

这段代码实例化了一个委托数组,并用两个函数签名符合的函数填充了该数组(就好比实例化类一样),然后分别取出这委托的这两个实例将其传递给另一个方法调用。
operations[i]表示这个委托,即这个委托代表的方法
operations[i](2.23)表示调用这个方法,参数放在括号中。

4、多播委托

多播委托允许把多个方法调用链接起来,成为一个列表。然后依次调用,一般委托的签名必须返回为void,不然就只能得到委托调用的最后一个方法的结果了。多播委托可以识别+、+=,-、-=等运算符。

多播委托示例如下:
class test    {        public static void one(double value)        {            double result = value * 2;            Console.WriteLine("One  :   the value is {0}, give you {1},",value,result);        }        public static void two(double value)        {            double result = value * value;            Console.WriteLine("Two  :   the value is {0}, give you {1},", value, result);        }    }    delegate void DoubleOperation(double value);//定义委托    class Program    {        static void Main(string[] args)        {            DoubleOperation opreations = test.one;//实例化委托            opreations += test.two;            display(opreations, 2.0);            display(opreations, 7.94);            display(opreations, 1.233);            Console.ReadLine();        }        static void display(DoubleOperation action, double value)        {            Console.WriteLine();            action(value);//该语句会按顺序调用action委托实例中的方法。        }    }


输出结果为:

使用多播委托应注意:
对同一个委托调用方法链的顺序并未正式定义,因此应避免编写依赖于以特定顺序调用方法的代码。
多播委托包含一个逐个调用的委托集合,如果通过委托调用的一个方法抛出异常,则整个迭代就会停止。即只执行了异常前的方法,停止在抛异常的方法那儿,之后的方法不再执行。
解决这个方法我们可以将委托链利用Delegate类定义的方法GetInvocationList()方法将其转换为Delegate对象数组,并循环一次执行相关方法,捕捉异常后,可继续下一次迭代。
如:
static void main(){    DemoDelegate dl = one;    dl+=two;    Delegate[] delegates=dl.GetInvocationList();    foreach(DemoDelegate d in delegates)    {        try            {            d();        }        catch(Exception)        {                    Console.WriteLine("Exception caught");        }    }}


5、匿名委托

    定义匿名委托时与前面相同,实例化时则不是传送已知的方法名,而是使用一个简单的代码块,它前面的关键字为delegate,后面括号中跟参数,大括号中跟实现代码。即:delegateName  test = delegate(string param){代码};//delegareName为定义的委托,test为该委托的实例
使用匿名方法应注意:
  •  匿名方法不能使用跳转语句跳到该匿名方法的外部,也不能从外部跳到匿名方法的内部。
  • 在匿名方法中不能访问不安全的代码。
  • 不能访问在匿名方法外部使用的ref和out参数化,当可以访问外部定义的其他变量
  • 若要多次编写同一个功能的的方法,就不要用匿名方法。