1、C#委托是什么
c#中的委托可以理解一种类,这种类实例化后可以将函数的包装成一个变量(该变量就变成了对该函数的“引用”),它使得这个变量(函数)可以作为参数来被传递,这在作用上相当于c中的函数指针。c用函数指针获取函数的入口地址,然后通过这个指针来实现对函数的操作。
委托的定义和方法的定义类似,只是在定义的前面多了一个delegate关键字。如下定义:
public delegate void MyDelegate(int para1, string para2);
委托能包装的方法是有一定限制的,例如能被前面的委托类型MyDelegate包装的方法需要满足以下条件:
1.方法的返回类型必须为void;
2.方法必须有两个参数,并且第一个参数应为int类型,第二个参数为string类型。
总结:可以被委托包装的方法必须满足以下规则:
1.方法的签名必须与委托一致,方法签名包括参数的个数、类型和顺序;
2.方法的返回类型要和委托一致,注意,方法的返回类型不属于方法签名的一部分。
2、委托的使用
//委托使用的演示 class Program { public delegate void MyDelegate(int para1, int para2); //1.使用delegate关键字来定义一个委托类型 static void Main(string[] args) { MyDelegate d; //2.声明委托变量d d = new MyDelegate(new Program().Add); //3.实例化委托类型,传递的方法也可以为静态方法,这里传递的是实例方法 MyMethod(d); //4.委托类型作为参数传递给另一个方法 Console.ReadKey(); } void Add(int para1, int para2) { int sum = para1 + para2; Console.WriteLine("两个数的和为:" + sum); } private static void MyMethod(MyDelegate mydelegate) { mydelegate(1, 2); //5.在方法中调用委托 } }
从以上代码可以看出,使用委托的步骤为:定义委托类型—声明委托变量—实例化委托—将实例作为参数传递给另一个方法—该方法调用委托。如下具体分析委托的使用过程。
(1)定义委托类型: public delegate void MyDelegate(int a, int b);。其定义方式类似于方法的定义,只是多了一个delegate关键字。
(2)声明委托变量:MyDelegate d;既然委托是一种类型,那么可以使用委托来声明一个委托变量,相当于int a;
(3)实例化委托: d = new MyDelegate(new Program().Add);。第二步只是声明了委托变量,但并没有将它实例化。类的实例化使用new关键字来实现,而委托也属于一种“类”,所以委托的实例化也使用new关键字来进行的。这里需要注意的是,委托的实例化是用一个方法名(不能带左右括号)作为参数,并且该方法的定义必须符合委托的定义,即该方法的返回类型、参数个数和类型必须与委托定义中的一样。这样,前面3步就好比构造了一个律师对象,而方法InstanceMethod好比是当事人的方法。
(4)作为参数传递给方法:MyMethod(d);。委托使用得在C#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。
(5)在方法中调用委托。MyMethod(d);。委托使用得在c#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。
总结:在使用委托时,需要注意以下几个问题。
1.在第三步中,被传递的方法的定义必须与委托定义相同,即方法的返回类型和参数个数、参数类型都必须与委托相同。并且,传递的是方法名,方法名后不能带有左右括号。
2.在第五步中,委托的调用与方法调用类似,传递的实参类型和个数必须与委托定义一致。
3.由于委托是方法的包装类型,所以对委托的调用也就是对其所包装的的方法的调用,上面第5步时间上是调用了Add方法来对传入的实参进行计算。
3、委托链的使用
委托链其实就是委托类型,只是委托链把多个委托链接在一起而已,也就是说,我们把链接了多个方法的委托称为委托链或多路广播委托。如下:
public delegate void DelegateTest(); static void Main(string[] args) { //用静态方法来实例化委托 DelegateTest dtstatic = new DelegateTest(Program.method1); DelegateTest dtinstance = new DelegateTest(new Program().method2); DelegateTest delegatechain = null; //定义一个委托对象,一开始初始化为null,即不代表任何方法。 //使用 “+”符号链接委托,链接多个委托后就成为了委托链 delegatechain += dtstatic; delegatechain += dtinstance; //调用委托链 delegatechain(); // 输出两行"这是静态方法"和"这是实例方法" Console.Read(); } private static void method1() { Console.WriteLine("这是静态方法"); } private void method2() { Console.WriteLine("这是实例方法"); }
从委托链中移除委托
//使用 “+”符号链接委托,链接多个委托后就成为了委托链 delegatechain += dtstatic; delegatechain += dtinstance; //使用 “-”运算符 移除委托 delegatechain -= dtstatic;
4、什么要使用委托
上一章中我们可能会很疑惑,为什么需要委托?为什么不直接在MyMethod方法里直接调用Add方法,反而要实例化一个委托对象来完成调用呢?这岂不是自找麻烦吗?
当然,c#引入委托并不是自找麻烦。委托是c#最好的一个特性,它为后来的很多特性都打下了基础。委托使得一个方法可以作为另一个方法的参数进行传递,这就是委托最大的作用。如下例子:
例如我们要实现一个打招呼的方法,而每个国家打招呼的方式都不一样,刚开始我们可能会像下面这样实现打招呼的方法:
public void Greeting(string name, string language) { switch (language) { case "zh-cn": ChineseGreeting(name); break; case "en-us": EnglishGreeting(name); break; default: EnglishGreeting(name); break; } } public void EnglishGreeting(string name) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); }
若后续我们需要添加德国、日本等打招呼方法,就必须修改Greeting方法内的case语句,来适应新的需求,这样特别不方便。有了委托,我们就可以把函数作为参数,并像如下代码实现Greeting方法:
public delegate void GreetingDelegate(string name); static void Main(string[] args) { //引入委托 Program p = new Program(); p.Greeting("小叶", p.ChineseGreeting); // 将所使用的的方法ChineseGreeting作为参数传递 p.Greeting("Tommy Li", p.EnglishGreeting); Console.Read(); } public void Greeting(string name, GreetingDelegate callback) { callback(name); // 调用ChineseGreeting方法 } public void EnglishGreeting(string name) { Console.WriteLine("Hello, " + name); } public void ChineseGreeting(string name) { Console.WriteLine("你好, " + name); }
文章转载自:【c# 学习笔记】c#委托是什么
5、例子
class Program { static void Main(string[] args) { Bookstore XinHua = new Bookstore(); // 实例化一个新化书店 Reader LiNing = new Reader(); // 实例化一个读者李宁 // 以new关键字实例化一个委托绑定到onpublish事件 // 传递的方法可以是“静态方法”,也可以是“实例方法” XinHua.onpublish += new Bookstore.publish(LiNing.issue); XinHua.issue(); Console.ReadKey(); } } public class Bookstore { public delegate void publish(); //声明委托 public event publish onpublish; //声明委托注册的事件 //声明触发方法 public void issue() { Console.WriteLine("书店发布了一本杂志"); this.onpublish(); } } public class Reader { // 定义收到杂志的方法 public void issue() { Console.WriteLine("我收到了一本杂志"); } }
运行结果:
示例2
class Program { static void Main(string[] args) { Bookstore XinHua = new Bookstore(); // 实例化一个新化书店 Reader LiNing = new Reader("李宁"); // 实例化一个读者李宁,订阅了电脑类杂志 Reader LiSi = new Reader("李四"); // 实例化一个读者李四,订阅了英语类杂志 // 李宁只订阅电脑类杂志事件 XinHua.on_publish_computer += new Bookstore.publish_computer(LiNing.received_book); // 李四两种杂志都订阅了 XinHua.on_publish_computer += new Bookstore.publish_computer(LiSi.received_book); XinHua.on_publish_english += new Bookstore.publish_english(LiSi.received_book); XinHua.issue_computer(); XinHua.issue_english(); Console.ReadKey(); } } public class Bookstore { // 委托有些类“似于”类的静态方法, 可以通过Bookstore.publish_computer进行访问 public delegate void publish_computer(string bookname); //声明电脑的委托 public event publish_computer on_publish_computer; //声明委托注册的事件 //发行电脑的触发方法 public void issue_computer() { Console.WriteLine("书店发布了一本电脑类杂志"); this.on_publish_computer("《电脑周报》"); } public delegate void publish_english(string bookname); //声明自己的委托 public event publish_english on_publish_english; //声明委托注册的事件 //发行电脑的触发方法 public void issue_english() { Console.WriteLine("书店发布了一本英语类杂志"); this.on_publish_english("《英语周报》"); } } public class Reader { public string name; public Reader(string n) { this.name = n; } // 定义收到杂志的方法 public void received_book(string bookname) { Console.WriteLine(this.name + "收到了一本杂志" + bookname); } }
运行结果:
示例3
class Program { static void Main(string[] args) { Bookstore XinHua = new Bookstore("新华书店"); // 实例化一个新化书店 Reader LiNing = new Reader("李宁"); // 实例化一个读者李宁,订阅了电脑类杂志 Reader LiSi = new Reader("李四"); // 实例化一个读者李四,订阅了英语类杂志 // 李宁和李四订阅电脑类杂志事件 XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiNing.received_book); XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiSi.received_book); // 李四订阅了英语类杂志事件 XinHua.PubEnglish += new Bookstore.PubEnglishEventHandler(LiSi.received_book); // 手动触发执行发布杂志事件 XinHua.issueComputer("电脑周刊", Convert.ToDateTime("2019.10.29")); XinHua.issueEnglish("英语周刊", Convert.ToDateTime("2018.09.29")); Console.ReadKey(); } } // 发布事件类 public class PubEventArgs : EventArgs { public string bookName; public DateTime bookTime; //构造函数 public PubEventArgs(string name, DateTime time) { this.bookName = name; this.bookTime = time; } } public class Bookstore { public string name; public Bookstore(string n) { this.name = n; } // 委托有些类似于类的静态方法, 可以通过Bookstore.PubComputerEventHandler进行访问 // 委托实际上是一种类,可以使用new关键字实例化 // 实例化后,就是对函数包装(引用),使得函数可以作为参数传递或赋值。 public delegate void PubComputerEventHandler(object sender, PubEventArgs e); //声明电脑的委托 public event PubComputerEventHandler PubComputer; //声明委托注册的事件 //发行电脑的触发方法 public void issueComputer(string bookname, DateTime booktime) { Console.WriteLine("书店发布了一本电脑类杂志"); this.PubComputer(this, new PubEventArgs(bookname, booktime)); } public delegate void PubEnglishEventHandler(object sender, PubEventArgs e); //声明自己的委托 public event PubEnglishEventHandler PubEnglish; //声明委托注册的事件 //发行电脑的触发方法 public void issueEnglish(string bookname, DateTime booktime) { Console.WriteLine("书店发布了一本英语类杂志"); this.PubEnglish(this, new PubEventArgs(bookname, booktime)); } } public class Reader { public string name; public Reader(string n) { this.name = n; } // 定义收到杂志的方法,参数不是一个简单的变量而是PubEventArgs实例 public void received_book(object sender, PubEventArgs e) { Bookstore bs = (Bookstore)sender; Console.WriteLine(string.Format("{0}收到了{1}书店发布的《{2}》 发布时间:{3}", this.name, bs.name, e.bookName, e.bookTime)); } }
允行结果: