第17章 委托

本章要讨论回调函数。回调函数是一种非常有用的编程机制,它的存在已经有很多年了。

Microsoft .Net Framework通过委托(delegate)来提供了一种回调函数机制。

列如:委托确保回调方法是类型安全的。委托还允许顺序调用多个方法,并支持调用静态方法和实例方法。

C#中委托是在程序运行时可以使用它们来调用不同的函数。

举个简单的例子,你是编程的,你现在正在写一个ASP.NET网页,而JS是你不熟悉的,于是你委托你的一位同事来帮助你完成JS部分。这就是委托,把你所不能做的事情交给其他人去做。

1.简单的委托http://www.cnblogs.com/birdshover/archive/2008/01/07/1029471.html

那么委托需要承载哪些信息呢?首先它存储了方法名,还有参数列表(方法签名),以及返回类型,比如:

delegate String/*返回类型*/ ProcessDelegate(Int32 i);

蓝色部分是声明委托的关键字,红色是返回类型,黑色部分是委托的类型名,()里的就是参数部分。你要使用这个委托来做事情,必须满足一下条件:

  • 返回类型和委托的返回类型一致,这里是String类型
  • 参数列表能且只能有一个参数,并且是Int32类型

例如:

输出的结果是:Text1Tex2

public delegate String ProcessDelegate(String s1, String s2);

class Program

{

    static void Main()

{

 //使用委托ProcessDelegate来调用Process方法

        ProcessDelegate pd = new ProcessDelegate(new Test().Process);

        Console.WriteLine(pd("Text1", "Text2"));

    }

}

public class Test

{

    public String Process(String s1, String s2)

    {

        return s1 + s2;

    }

}

2.回调函数

回调函数就是把一个方法传给另一个方法去执行。它与委托不同在于,它的方法参数,返回值都可以和调用者的参数,返回值可以不一样。

输出结果:

Text1Text2

Text1

Text2

Text2Text1

public delegate String ProcessDelegate(String s1, String s2);

class Program

{

    static void Main()

    {

        Test t = new Test();

        //Process方法(调用者)调用了一个回调函数Process1,当然这里只执行了回调函数。

        //可以看出,可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。

        //将Process1 2 3方法传递给Process方法去执行

        string r1 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process1));

        string r2 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process2));

        string r3 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process3));

        Console.WriteLine(r1);

        Console.WriteLine(r2);

        Console.WriteLine(r3);

    }

}

public class Test

{

    public String Process(String s1, String s2, ProcessDelegate process)

    {

        return process(s1, s2);

    }

    public String Process1(String s1, String s2)

    {

        return s1 + s2;

    }

    public String Process2(String s1, String s2)

    {

        return s1 + Environment.NewLine + s2;

    }

    public String Process3(String s1, String s2)

    {

        return s2 + s1;

    }

}

17.1初识委托

以下代码演示了如何声明、创建和使用委托:

using System;

using System.Windows.Forms;

using System.IO;

namespace WindowsFormsApplication1

{

    //声明一个委托类型,它的实例引用一个方法

    //指定一个回调函数的签名,该方法获取一个Int32参数,返回void

    internal delegate void Feedback(Int32 value);

    public sealed class Program

    {

        public static void Main()

        {

            StaticDelegateDemo();

            InstanceDelegateDemo();

            ChainDelegateDemo1(new Program());

            ChainDelegateDemo2(new Program());

        }

        public static void StaticDelegateDemo()

        {

            Console.WriteLine("----Static Delegate Demo----");

            Counter(1, 3, null);

            //前缀Program可选

            Counter(1, 3, new Feedback(Program.FeedbackToConsole));

            Counter(1, 3, new Feedback(FeedbackToMsgBox));

            Console.WriteLine();

        }

        private static void InstanceDelegateDemo()

        {

            Console.WriteLine("----Instance Delegate Demo----");

            Program p = new Program();

            Counter(1, 3, new Feedback(p.FeedbackToFile));

            Console.WriteLine();

        }

        private static void ChainDelegateDemo1(Program p)

        {

            Console.WriteLine("----Chain Delegate Demo 1----");

            Feedback fb1 = new Feedback(FeedbackToConsole);

            Feedback fb2 = new Feedback(FeedbackToMsgBox);

            Feedback fb3 = new Feedback(p.FeedbackToFile);

            Feedback fbChain = null;

            fbChain = (Feedback)Delegate.Combine(fbChain, fb1);

            fbChain = (Feedback)Delegate.Combine(fbChain, fb2);

            fbChain = (Feedback)Delegate.Combine(fbChain, fb3);

            Counter(1, 2, fbChain);

            Console.WriteLine();

            fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));

            Counter(1, 2, fbChain);

        }

        private static void ChainDelegateDemo2(Program p)

        {

            Console.WriteLine("----Chain Delegate Demo 2----");

            Feedback fb1 = new Feedback(FeedbackToConsole);

            Feedback fb2 = new Feedback(FeedbackToMsgBox);

            Feedback fb3 = new Feedback(p.FeedbackToFile);

            Feedback fbChain = null;

            fbChain += fb1;

            fbChain += fb2;

            fbChain += fb3;

            Counter(1, 2, fbChain);

            Console.WriteLine();

            fbChain -= new Feedback(FeedbackToMsgBox);

            Counter(1, 2, fbChain);

        }

        private static void Counter(Int32 from, Int32 to, Feedback fb)

        {

            for (Int32 val = from; val <= to; val++)

            {

                //如果指定了任何回调函数,就调用它们

                if (fb != null)

                    fb(val);

            }

        }

        private static void FeedbackToConsole(Int32 value)

        {

            Console.WriteLine("Item=" + value);

        }

        private static void FeedbackToMsgBox(Int32 value)

        {

            MessageBox.Show("Item=" + value);

        }

        private void FeedbackToFile(Int32 value)

        {

            StreamWriter sw = new StreamWriter("Status", true);

            sw.WriteLine("Item=" + value);

            sw.Close();

        }

    }

}

17.2用委托回调静态方法

在StaticDelegateDemo方法中,第一次调用Counter方法时,为第三个参数传递的是null。由于Counter的fb参数收到的是null,所以每个数据项在处理时,都不会调用回调函数。

接着StaticDelegateDemo方法再次调用Counter方法,为第三个参数传递一个新构造的Feedback委托对象。委托对象(new操作符新建的Feedback对象)是方法的一个包装器(wrapper),使方法能通过包装器来间接回调。

在本例中,静态方法的完整名称Program.FeedbackToConsole被传给Feedback委托类型的构造器。表明FeedbackToConsole就是要包装的方法。new操作符返回的引用作为Counter的第三个参数来传递。

在一个类型中,可以通过委托来调用另一个类型的私有成员时,只要委托对象是有具有足够安全性和可访问性的代码创建时,便不会有问题。

这个例子中的所有操作都是类型安全的。例如,在构造Feedback委托对象时,编译器确保Program的FeedbackToConsole方法的签名,兼容于Feedback委托定义的签名。具体的说,FeedbackToConsole必须获取一个参数,而且两者都必须有相同的返回类型(void)。

将一个方法绑定到委托时,C#和CLR都允许引用类型的协变形和逆变性。

17.3用委托回调实例方法

InstanceDelegateDemo中构造了一个名为p的Program对象。这个Program对象没有定义任何实例字段和属性。向Counter委托类型的构造函数传递的是p.FeedbackToFile,这导致委托包装对FeedbackToFile方法的一个引用,这个方法是实例方法,而不是静态方法。当Counter调用由其fb实参标识的回调函数时,会调用FeedbackToFile实例方法。

17.4委托揭秘

从表面看,委托似乎很容易使用:用C#的delegate关键字,用熟悉的new操作符构造委托实例。

CLR和编译器做了大量的工作来隐藏委托的复杂性。

原文地址:https://www.cnblogs.com/chrisghb8812/p/5618307.html