委托是C#中比较重要的概念,学习C#在这里最容易产生迷惑,理解事后对后面的学习颇有帮助。
有些时候,因为咱们在开发程序时对后续可能出现的要求及变化考虑不足而致使麻烦,这些新变化可能致使程序的从新编写,那能不能改变这种状况?后面的需求变化了,后续对应功能的编写对前面的程序不形成影响?
能够的,在C#中能够用委托来解决这个问题。
举个简单的例子。
好比一个数据表须要导出,咱们在开始只是设计了导出到TXT和EXCEL,程序以下:
//定义类及使用方法
class HrDataInfo
{
//定义委托
public delegate void SaveAsDelegate(string FileName);
public SaveAsDelegate SaveAs;
public void SaveAsTxt(string FileName)
{
Console.WriteLine("将数据导出到TXT文件!{0}",FileName);
}
public void SaveAsExcel(string FileName)
{
Console.WriteLine("将数据导出到EXCEL文件!{0}",FileName);
}
}
上面定义了一个HrDataInfo类,类中有两个方法,一个是SaveAsTxt(string FileName),一个是SaveAsExcel(string FileName),如何让用户来决定使用哪个呢?
玄机就在于上面的两行代码:
public delegate void SaveAsDelegate(string FileName);
public SaveAsDelegate SaveAs;
定义一个委托,先保证和定义方法同样,等于定义一个统一的方法模子SaveAsDelegate(string FileName),委托实质上是类,由于不是静态的因此要实例化为SaveAs。
//使用
class Program
{
static void Main(string[] args)
{
HrDataInfo Hr = new HrDataInfo();
//输出到Excel文件
Hr.SaveAs = Hr.SaveAsExcel;
Hr.SaveAs(@"C:\1.xls");
//输出到TXT文件
Hr.SaveAs = Hr.SaveAsTxt;
Hr.SaveAs(@"C:\1.TXT");
//输出到Word文件,是静态方法,直接使用
Hr.SaveAs = SaveAsWord;
Hr.SaveAs(@"C:\1.Word");
//输出到PPT文件,非静态,实例化对象后引用
OthersDealWith ODW=new OthersDealWith ();
Hr.SaveAs =ODW.SaveAsPPT;
Hr.SaveAs(@"C:\1.PPT");
Console.ReadKey();
}
static void SaveAsWord(string FileName)
{
Console.WriteLine("将数据导出到Word文件!{0}",FileName);
}
}
public class OthersDealWith
{
public void SaveAsPPT(string FileName)
{
Console.WriteLine("将数据导出到PPT文件!{0}",FileName);
}
}
由于原来的类中只有导出到Excel和TXT,如今须要导出到Word和PPT,而原来的类中没有这个方法,怎么办?
写上相应的处理方法,而后赋值给委托就好了。
//另外的写法
Hr.SaveAs = new HrDataInfo.SaveAsDelegate(Hr.SaveAsExcel);
Hr.SaveAs(@"C:\1.xls");
上面也是对的,是通常的写法。
总结:
1、委托实际上就是指针,就是方法的地址,程序中你让它指向哪一个方法它就指向哪一个方法;
2、委托是统一的方法模型,参数必须一致;
3、委托其实是把方法看成参数来传递,能够是静态方法也能够是非静态的方法。
从上面的例子中看出,类中定义了委托给程序带来了很大的灵活性,有一个类放在那里,里面藏了个指针,你让它指向哪里它就指向哪里(固然有约定)。这让咱们想到了事件,好比一个按钮放在窗体上,若是里面也藏了这么个东西,是否是就能够处理相应的点击,好比,你点击了按钮,我让它指向一个处理程序,那么这个是否是就有了所谓的按钮响应,其实,这就是事件,叫按钮的点击事件。
C#中是否是这样处理的呢?
新键一个窗体程序,随便放一个按钮button1到窗体form1上,在咱们没有为按钮写处理程序(鼠标点击)以前,看Form1.Designer.cs中的代码是这样的:
this.button1.Location = new System.Drawing.Point(29, 51);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
如今,咱们双击按钮button1,为它编写鼠标点击事件,看看系统为咱们作了什么?
第一个变化,系统自动加了以下的代码:
private void button1_Click(object sender, EventArgs e)
{
}
第二个变化,仍是看Form1.Designer.cs,
this.button1.Location = new System.Drawing.Point(29, 51);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
对比上面的代码,多了
this.button1.Click += new System.EventHandler(this.button1_Click);
(+=,-=,+,-,是委托的运算,+和+=是订阅,-和-=是退订)
原来就是这样的:this.button1.Click就是个委托,只是系统自动地把它指向了button1_Click(object sender, EventArgs e),因此,用户在按钮Button上点鼠标系统就自动运行button1_Click(object sender, EventArgs e)里面的程序。
也能够改动这些系统自动生成的代码(通常建议不要动)。
好比,写以下程序:
private void MineBT1_Click(object sender, EventArgs e)
{
MessageBox.Show("你点击了按钮!");
}
而后改动Form1.Designer.cs中的代码 把 this.button1.Click += new System.EventHandler(this.button1_Click); 替换为 this.button1.Click+=this.MineBT1_Click; 运行程序,同样获得的结果。 另外,在WPF程序中,还能够经过属性赋值的方式来把事件处理程序与事件的拥有者联系在一块儿,好比你在窗体上放置一个按钮,只要你为这个按钮的单击事件写了响应程序,系统自动为你把两者结合起来[绑定],就是属性赋值。 Click="button1_Click" 固然你也能够在程序中删除它,经过C#语言来处理。 在类中写下: this.button1.Click+=new RouteDEventHandler(button1_Click); 效果和属性赋值是同样的。 这自定义控件中,如何定义事件响应,好比用户作了个自定义控件,里面有个按钮,它想让用户点击这个按钮时运行用户所写的代码,由于控件已经封装好了,里面定义好按钮的事件,在控件里面或者外面写处理方法,按照上面的模式处理就OK了。