【什么是委托】
- 委托是对函数的引用,它是一个引用类型,类似c/cpp中的函数指针。但它是类型安全的。
- 委托是一个类,定义了方法的类型,可以将方法当做另一个方法的参数传递。
委托就是一个安全的函数指针,用来执行函数方法的东西。
【如何使用委托】
在.Net框架下,委托的使用方法经历了多次改变。
最初委托的使用方法如下:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 | public delegate string MyDelegate(string name, int age);
static void Main(string[] args)
{
    MyDelegate md = new MyDelegate(Show);
    Console.Write(md("Joe",20));
}
private static string Show(string name, int age)
{
    return "Hello!" + name + ":" + age;
}
 | 
 
可以看到使用委托的方法是:
1.定义委托,格式为:
delegate 返回值 委托名(参数…)
2.定义委托可以调用的方法,这些方法的返回值和参数(类型,个数)必须与委托声明的返回值和参数一致。这些函数方法既可以是静态,也可以是非静态。而c/cpp中的函数指针只能调用静态方法。例子中使用的是名为Show的静态函数。当然我们也可以新建一个Test类,写一个非静态方法Show1,保证它的返回值和参数与委托一致即可。
3.实例化委托,委托与类不同,类实例化后产生一个对象,但委托实例化后仍是一个委托,可以叫他委托实例也可以叫委托对象。它的实例化格式与类的实例化很相近。
委托名 实例名 = new 委托名(方法名)
如果使用委托调用第2步中建立的Show1方法,则应是:
| 1
 | MyDelegate md = new MyDelegate(new Test().Show1);
 | 
 
4.使用委托十分简单,直接操作实例化后的委托,并传入参数就行了。
实例名(参数……)
在.Net2.0后,加入了泛型委托,.Net3.5又加入了lambda表达式。这时候委托的使用方法变的更为简单:
.Net3.5加入的两个泛型委托是Action和Func,其中Action相当于无返回值的委托而Func是有返回值的委托。
他们的声明方式为:
Func<参数1类型,参数2类型……,返回值类型>
Action<参数1类型,参数2类型……>
如果使用泛型委托,那么上面的例子则变为:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 | public static Func<string, int, string> myfunc;
static void Main(string[] args)
{
    myfunc = new Func<string, int, string>(Show);
    Console.Write(myfunc("Joe", 20));
}
private static string Show(string name, int age)
{
    return "Hello!" + name + ":" + age;
}
 | 
 
如果使用lambda表达式,上面的例子将变得更简单:
| 1
2
3
4
5
6
7
8
 | public static Func<string, int, string> myfunc = (string name, int age) =>
{
    return "Hello!" + name + ":" + age;
};
static void Main(string[] args)
{
    Console.Write(myfunc("Joe", 20));
}
 | 
 
但是这样写过之后会有一个问题,我们把定义委托,实例化,调用方法全写在一起了,代码是简单了不少,不过委托却只能调用这一个方法了。如果重新实例化并调用,那么则会覆盖掉上一个委托实例。可以看看下面程序的运行结果:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 | public static Func<string, int, string> myfunc = (string name, int age) =>
{
    return "Hello!" + name + ":" + age;
};
static void Main(string[] args)
{
    Console.Write(myfunc("Joe", 20));
    myfunc = new Func<string, int, string>(Show);
    Console.Write(myfunc("Joe", 20));
}
private static string Show(string name, int age)
{
    return "Nice to meet you!" + name + ":" + age;
}
 | 
 
所以说怎么使用委托,需要根据情况来选择。
【为什么要使用委托】
使用委托可以将函数方法封装在委托对象内,委托可以将一个函数作为一个参数变量在程序中传递。
根据这一个作用,我们平时可以用委托启动线程,通用类库,注册事件等等。
这里提两个重要用法:
1.多路广播委托
前面的例子中委托只包含了一个方法的调用,如果要调用多个方法,就要多次显示的重新实例化委托并调用方法。事实上通过多路广播委托,可以让委托包含多个方法。其操作方法是:通过“+=”向委托添加调用方法。通过“-=”删除委托中的方法,有点类似事件的注册。
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 | 
public delegate void myDelegate(string str);
class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        myDelegate md = new myDelegate(t.Func1);
        md("Before += Fun2");
        md += t.Func2;
        md("After += Fun2 and Before -= Fun1");
        md -= t.Func1;
        md("After -= Fun1");
    }
}
public class Test
{
    public void Func1(string str)
    {
        Console.WriteLine("Func1:" + str);
    }
    public void Func2(string str)
    {
        Console.WriteLine("Func2:" + str);
    }
}
 | 
 
需要注意的是,多路广播委托的返回值需要为void,因为返回值不知道返回到什么地方。
2.跨线程调用
在WPF中如果有下面的场景:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 | public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Thread thread = new Thread(new ThreadStart(TestThread));
        thread.Start();
    }
    private void TestThread()
    {
        label.Content = "hello";
    }
}
 | 
 
即在不同线程中调用控件,那么会出现下面的错误:

这时候通过委托就可以实现跨线程访问:
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
 | 
public delegate void myDelegate(string msg);
public MainWindow()
{
    InitializeComponent();
    Thread thread = new Thread(new ThreadStart(TestThread));
    thread.Start();
}
private void TestThread()
{
    myDelegate md = new myDelegate(Show);
    label.Dispatcher.Invoke(md,"Hello");
}
private void Show(string msg)
{
    label.Content = msg;
}
 |