C#中参数的传递分为值传递和引用传递,在C中,对于引用的传递是没有限制的,方法(函数)也可进行传递,只需在接受的程序中将传递过来的数据用指针进行接收,然后通过该指针进行方法(函数)的调用即可。在C#中增加了对类型安全的限制,方法不能再作为普通的引用进行传递,因此C#中新增了委托,用于方法引用的传递。
委托实际上是一个封装过的类。最终派生自基类System.Delegate,C#对委托的使用进行了简化,使其的使用过程类似于基本类型。但是和基本类型的使用有着天壤之别,最突出的是委托类型作为一个独立的类的声明是不能放在在方法中的。
delegate string PrintDele(string name); static void DelegateDemo() { PrintDele deleDemo = new PrintDele(new Program().GetHello); Console.WriteLine("Main Print " + deleDemo("test1")); Console.WriteLine("Main Print " + deleDemo.Invoke("test2")); } string GetHello(string name) { return "Hello" + name; }
运行结果为:
C#对委托的使用进行了进一步的简化,使委托在使用之前不用进行delegate声明,这就是Action和Func,Action用于替代有N个参数,无返回值的委托声明,Func用于替代有N的参数,有返回值的委托声明。使用时Action<string>中string表示其委托的方法使用的参数类型,也可以为Action<string,string,int>等方式,最多为16种方式。Func<string,string>中第一个String表示其委托的方法的参数类型,第二个string表示其返回值类型,也可以为Func<string,,int,string,bool>等,最后一个参数均表示其返回值类型
static void DelegateDemo() { Action<string> action1 = new Action<string>(new Program().PrintHello); action1("test action"); Func<string,string> func1 = new Func<string,string>(new Program().GetHello); Console.WriteLine("Main Print from func " + func1("test1")); } string GetHello(string name) { return "Hello" + name; } void PrintHello(string name) { Console.WriteLine("Hello" + name); }
C#对委托的更近一步简化为匿名函数,即可以不声明方法名,只给出方法体:
action1 = new Action<string>(delegate(string name) { Console.WriteLine("Hello" + name); });action1();
使用delegate作为关键字,在具体编译后编译器会把匿名函数提出并自动分配方法名
3.0以后C#支持使用Lambda表达式替代匿名方法,如:
action1 = new Action<string>((string name) => { Console.WriteLine("Hello" + name); });action1();
其实和匿名函数完全相同
上面所有的例子中一个委托只针对一个方法,其实一个委托可以包含多个方法,当调用委托时这些方法均会被执行,返回值为最后一个执行的方法的返回值,称为多播委托,但是由于多播委托中方法的执行顺序是不被保证的,所以一般多播委托只用于委托没有返回值的方法
action1 = new Action<string>((string name) => { Console.WriteLine("Hello" + name+"action1"); }); action1 += new Action<string>((string name) => { Console.WriteLine("Hello" + name+"action2"); }); action1 += new Action<string>((string name) => { Console.WriteLine("Hello" + name+"action3"); }); action1("lambda action");
委托引申出事件,其实事件也只是委托的一种实现形式,针对事件的编程有著名的ObServer设计模式,代码如下:
class ObServer { public event EventHandler ChangeValueHandler; public string Value { get; private set; } public ObServer(string value) { this.Value = value; } public void SetValue(string value) { this.Value = value; ChangeValueHandler(this, new EventArgs()); } }
class Listener { private string serverValue; private string name; public Listener(ObServer server,string name) { this.serverValue = server.Value; this.name = name; } public void ServerChangeValue(object sender,EventArgs args) { ObServer server = sender as ObServer; if (server != null) { this.serverValue = server.Value; Console.WriteLine("--"+name+"--server value change to " + serverValue); } } }
static void Main(string[] args) { ObServer server = new ObServer("first value"); Listener listener1 = new Listener(server,"Lis1"); Listener listener2 = new Listener(server, "Lis2"); server.ChangeValueHandler += listener1.ServerChangeValue; server.ChangeValueHandler += new EventHandler(listener2.ServerChangeValue); Console.WriteLine("--change value--"); server.SetValue("second Value"); Console.WriteLine("--change value--"); server.SetValue("thre Value"); Console.ReadLine(); }
最后的执行结果为: