委托是C#中比较重要的概念,学习C#在这里最容易产生迷惑,理解过后对后面的学习很有帮助。
有些时候,由于我们在开发程序时对后续可能出现的要求及变化考虑不足而导致麻烦,这些新变化可能导致程序的重新编写,那能不能改变这种情况?后面的需求变化了,后续对应功能的编写对前面的程序不造成影响?
可以的,在C#中可以用委托来解决这个问题。
举个简单的例子。
比如一个数据表需要导出,我们在开始只是设计了导出到TXT和EXCEL,程序如下:
//定义类及使用方法
class HrDataInfo
{
//定义委托
public delegate void SaveAsDelegate(string FileName);
public SaveAsDelegate SaveAs;public void SaveAsTxt(string FileName)<br /> {<br /> Console.WriteLine("将数据导出到TXT文件!{0}",FileName);<br /> }
public void SaveAsExcel(string FileName)<br /> {<br /> Console.WriteLine("将数据导出到EXCEL文件!{0}",FileName);<br /> }<br /> }<br /> 上面定义了一个HrDataInfo类,类中有两个方法,一个是SaveAsTxt(string FileName),一个是SaveAsExcel(string FileName),如何让用户来决定使用哪一个呢?<br /> 玄机就在于上面的两行代码:<br /> public delegate void SaveAsDelegate(string FileName);<br /> public SaveAsDelegate SaveAs;<br /> 定义一个委托,先保证和定义方法一样,等于定义一个统一的方法模子SaveAsDelegate(string FileName),委托实质上是类,因为不是静态的所以要实例化为SaveAs。<br /> //使用<br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> HrDataInfo Hr = new HrDataInfo();<br /> //输出到Excel文件<br /> Hr.SaveAs = Hr.SaveAsExcel;<br /> Hr.SaveAs(@"C:\1.xls");<br /> //输出到TXT文件<br /> Hr.SaveAs = Hr.SaveAsTxt;<br /> Hr.SaveAs(@"C:\1.TXT");<br /> //输出到Word文件,是静态方法,直接使用<br /> Hr.SaveAs = SaveAsWord;<br /> Hr.SaveAs(@"C:\1.Word");<br /> //输出到PPT文件,非静态,实例化对象后引用<br /> OthersDealWith ODW=new OthersDealWith ();<br /> Hr.SaveAs =ODW.SaveAsPPT;<br /> Hr.SaveAs(@"C:\1.PPT");
Console.ReadKey();<br /> }
static void SaveAsWord(string FileName)<br /> {<br /> Console.WriteLine("将数据导出到Word文件!{0}",FileName);<br /> } }
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)
{}<br /> 第二个变化,还是看Form1.Designer.cs,<br /> this.button1.Location = new System.Drawing.Point(29, 51);<br /> this.button1.Name = "button1";<br /> this.button1.Size = new System.Drawing.Size(75, 23);<br /> this.button1.TabIndex = 0;<br /> this.button1.Text = "button1";<br /> this.button1.UseVisualStyleBackColor = true;<br /> this.button1.Click += new System.EventHandler(this.button1_Click);<br /> 对比上面的代码,多了<br /> this.button1.Click += new System.EventHandler(this.button1_Click);<br /> (+=,-=,+,-,是委托的运算,+和+=是订阅,-和-=是退订)<br /> 原来就是这样的:this.button1.Click就是个委托,只是系统自动地把它指向了button1_Click(object sender, EventArgs e),所以,用户在按钮Button上点鼠标系统就自动运行button1_Click(object sender, EventArgs e)里面的程序。<br /> 也可以改动这些系统自动生成的代码(一般建议不要动)。<br /> 比如,写如下程序:<br /> private void MineBT1_Click(object sender, EventArgs e)<br /> {<br /> MessageBox.Show("你点击了按钮!");<br /> } 然后改动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了。
|