若是咱们要把方法当作参数来传递的话,就要用到委托。简单来讲委托是一个类型,这个类型能够赋值一个方法的引用。编程
在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方法组成的,而后使用这个类实例化对象。在咱们使用委托的时候,也须要通过这两个阶段,首先定义委托,告诉编译器咱们这个委托能够指向哪些类型的方法,而后,建立该委托的实例。设计模式
定义委托的语法以下: 函数
delegate void IntMethodInvoker(int x);
定义了一个委托叫作IntMethodInvoker, 这个方法要带有一个int类型的参数,而且方法的返回值是void的。 定义一个委托要定义方法的参数和返回值,使用关键字delegate定义。this
private delegate string GetAString(); static void Main(){ int x = 40; GetAString firstStringMethod = new GetAString(x.ToString); Console.WriteLine(firstStringMethod()); }
在这里咱们首先使用GetAString委托声明了一个类型叫作fristStringMethod,接下来使用new 对它进行初始化,使它引用到x中的ToString方法上,这样firstStringMethod就至关于x.ToString,咱们经过firstStringMethod()执行方法就至关于x.ToString()spa
经过委托示例调用方法有两种方式设计
fristStringMethod(); firstStringMethod.Invoke();
只须要把方法名给一个委托的构造方法就能够了code
GetAString firstStringMethod = new GetAString(x.ToString);
也能够把方法名直接给委托的实例对象
GetAString firstStringMethod = x.ToString;
除了咱们本身定义的委托以外,系统还给咱们提供过来一个内置的委托类型,Action和Func事件
Action委托引用了一个void返回类型的方法,T表示方法参数编译器
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _005_Action委托 { class Program { static void PrintString() { Console.WriteLine("hello world."); } static void PrintInt(int i) { Console.WriteLine(i); } static void PrintString(string str) { Console.WriteLine(str); } static void PrintDoubleInt(int i1, int i2) { Console.WriteLine(i1+i2); } static void Main(string[] args) { //Action a = PrintString;//action是系统内置(预约义)的一个委托类型,它能够指向一个没有返回值,没有参数的方法 //Action<int> a=PrintInt;//定义了一个委托类型,这个类型能够指向一个没有返回值,有一个int参数的方法 //Action<string> a = PrintString;//定义了一个委托类型,这个类型能够指向一个没有返回值,有一个string参数的方法 在这里系统会自动寻找匹配的方法 Action<int, int> a = PrintDoubleInt; a(34, 23); Console.ReadKey(); //action能够后面经过泛型去指定action指向的方法的多个参数的类型 ,参数的类型跟action后面声明的委托类型是对应着的 } } }
Func引用了一个带有一个返回值的方法,它能够传递0或者多到16个参数类型,和一个返回类型
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _006_Func委托 { class Program { static int Test1() { return 1; } static int Test2(string str) { Console.WriteLine(str); return 100; } static int Test3(int i, int j) { return i + j; } static void Main(string[] args) { //Func<int> a = Test1;//func中的泛型类型制定的是 方法的返回值类型 //Console.WriteLine(a()); //Func<string, int> a = Test2;//func后面能够跟不少类型,最后一个类型是返回值类型,前面的类型是参数类型,参数类型必须跟指向的方法的参数类型按照顺序对应 Func<int, int, int> a = Test3;//func后面必须指定一个返回值类型,参数类型能够有0-16个,先写参数类型,最后一个是返回值类型 int res = a(1, 5); Console.WriteLine(res); Console.ReadKey(); } } }
前面使用的委托都只包含一个方法的调用,可是委托也能够包含多个方法,这种委托叫作多播委托。使用多播委托就能够按照顺序调用多个方法,多播委托只能获得调用的最后一个方法的结果,通常咱们把多播委托的返回类型声明为void。
Action action1 = Test1; action2+=Test2; action2-=Test1;
多播委托包含一个逐个调用的委托集合,若是经过委托调用的其中一个方法抛出异常,整个迭代就会中止。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _008_多播委托 { class Program { static void Test1() { Console.WriteLine("test1"); //throw new Exception(); } static void Test2() { Console.WriteLine("test2"); } static void Main(string[] args) { //多播委托 Action a = Test1; a += Test2;//表示添加一个委托的引用 //a -= Test1; //a -= Test2; //if(a!=null) // a();//当一个委托没有指向任何方法的时候,调用的话会出现异常null // 取得多播委托中全部方法的委托 Delegate[] delegates = a.GetInvocationList(); foreach (Delegate de in delegates) { de.DynamicInvoke(); } Console.ReadKey(); } } }
到目前为止,使用委托,都是先定义一个方法,而后把方法给委托的实例。但还有另一种使用委托的方式,不用去定义一个方法,应该说是使用匿名方法(方法没有名字)。
Func<int,int,int> plus = delegate (int a,int b){ int temp = a+b; return temp; }; int res = plus(34,34); Console.WriteLine(res);
在这里至关于直接把要引用的方法直接写在了后面,优势是减小了要编写的代码,减小代码的复杂性
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _009_匿名方法 { class Program { static int Test1(int arg1, int arg2) { return arg1 + arg2; } static void Main(string[] args) { //Func<int, int, int> plus = Test1; //修改为匿名方法的形式 Func<int, int, int> plus = delegate(int arg1, int arg2) { return arg1 + arg2; }; //匿名方法 本质上是一个方法,只是没有名字,任何使用委托变量的地方均可以使用匿名方法赋值 Console.ReadKey(); } } }
从C#3.0开始,可使用Lambda表达式代替匿名方法。只要有委托参数类型的地方就可使用Lambda表达式。刚刚的例子能够修改成
Func<int,int,int> plus = (a,b)=>{ int temp= a+b;return temp; }; int res = plus(34,34); Console.WriteLine(res);
Lambda运算符“=>”的左边列出了须要的参数,若是是一个参数能够直接写 a=>(参数名本身定义),若是多个参数就使用括号括起来,参数之间以,间隔。
若是Lambda表达式只有一条语句,在方法快内就不须要花括号和return语句,编译器会自动添加return语句。若是Lambda表达式的实现代码中须要多条语句,就必须添加花括号和return语句。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _010_Lambda表达式 { class Program { static void Main(string[] args) { //lambda表达式用来代替匿名方法,因此一个lambda表达式也是定义了一个方法 //Func<int, int, int> plus = delegate(int arg1, int arg2) { // return arg1 + arg2; //}; //Func<int, int, int> plus = (arg1, arg2) =>// lambda表达式的参数是不须要声明类型的 //{ // return arg1 + arg2; //}; //Console.WriteLine(plus(90,60)); Func<int, int> test2 = a => a+1;//lambda表示的参数只有一个的时候,能够不加上括号 当函数体的语句只有一句的时候,咱们能够不加上大括号 也能够不加上return语句 Func<int, int> test3 = (a) => { return a + 1; }; Console.WriteLine(test2(34)); Console.WriteLine(test3(34)); Console.ReadKey(); } } }
经过Lambda表达式能够访问Lambda表达式块外部的变量。这是一个很是好的功能,但若是不能正确使用,也会很是危险。示例:
int somVal = 5; Func<int,int> f = x=>x+somVal; Console.WriteLine(f(3));//8 somVal = 7; Console.WriteLine(f(3));//10
这个方法的结果,不但受到参数的控制,还受到somVal变量的控制,结果不可控,容易出现编程问题,用的时候要谨慎。
事件(event)基于委托,为委托提供了一个发布/订阅机制,咱们能够说事件是一种具备特殊签名的委托。
好比:咱们按键点击,这就是一个事件,在其它地方接收到这个事件,触发相应。
下面使用猫和老鼠的实例来演示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 { /// <summary> /// 猫类 /// </summary> class Cat { private string name; private string color; public Cat(string name, string color) { this.name = name; this.color = color; } /// <summary> /// 猫进屋(猫的状态发生改变)(被观察者的状态发生改变) /// </summary> public void CatComing() { Console.WriteLine(color+"的猫"+name+"过来了,喵喵喵 ..."); if(catCome!=null) catCome(); } public event Action catCome;//声明一个事件 发布了一个消息 } }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 { /// <summary> /// 观察者类:老鼠 /// </summary> class Mouse { private string name; private string color; public Mouse(string name, string color,Cat cat) { this.name = name; this.color = color; cat.catCome += this.RunAway;//把自身的逃跑方法 注册进 猫里面 订阅消息 } /// <summary> /// 逃跑功能 /// </summary> public void RunAway() { Console.WriteLine(color+"的老鼠"+name+"说: 老猫来, 赶忙跑, 我跑, 我使劲跑,我加速使劲跑 ..."); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 { class Program { static void Main(string[] args) { Cat cat = new Cat("加菲猫","黄色"); Mouse mouse1 = new Mouse("米奇","黑色",cat); Mouse mouse2 = new Mouse("水", "红色",cat); cat.CatComing(); //cat.catCome();//事件不能再类的外部触发,只能在类的内部触发 Console.ReadKey(); } } }