C#委托学习笔记
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));
}
}
允行结果: