.Net委托详解
【什么是委托】
- 委托是对函数的引用,它是一个引用类型,类似c/cpp中的函数指针。但它是类型安全的。
- 委托是一个类,定义了方法的类型,可以将方法当做另一个方法的参数传递。
委托就是一个安全的函数指针,用来执行函数方法的东西。
【如何使用委托】
在.Net框架下,委托的使用方法经历了多次改变。
最初委托的使用方法如下:
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方法,则应是:
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类型……>
如果使用泛型委托,那么上面的例子则变为:
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表达式,上面的例子将变得更简单:
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)); }
但是这样写过之后会有一个问题,我们把定义委托,实例化,调用方法全写在一起了,代码是简单了不少,不过委托却只能调用这一个方法了。如果重新实例化并调用,那么则会覆盖掉上一个委托实例。可以看看下面程序的运行结果:
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.多路广播委托
前面的例子中委托只包含了一个方法的调用,如果要调用多个方法,就要多次显示的重新实例化委托并调用方法。事实上通过多路广播委托,可以让委托包含多个方法。其操作方法是:通过“+=”向委托添加调用方法。通过“-=”删除委托中的方法,有点类似事件的注册。
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中如果有下面的场景:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Thread thread = new Thread(new ThreadStart(TestThread)); thread.Start(); } private void TestThread() { label.Content = "hello"; } }
即在不同线程中调用控件,那么会出现下面的错误:
这时候通过委托就可以实现跨线程访问:
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; }