文章是转载的,应该很容易百度到,但是学习的过程中加入了一些自己的理解和体会吧。
引言:
对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以有了这个系列,希望通过这个系列让朋友对C#的基础知识理解能够更进一步。然而委托又是C#基础知识中比较重要的一点,基本上后面的特性都和委托有点关系,所以这里就和大家先说说委托,为什么我们需要委托。
一、C#委托是什么的?
在正式介绍委托之前,我想下看看生活中委托的例子——生活中,如果如果我们需要打官司,在法庭上是由律师为我们辩护的,然而律师真正执行的是当事人的陈词,这时候律师就是一个委托对象,当事人委托律师这个对象去帮自己辩护。这就是我们生活中委托的例子的。然而C#中委托的概念也就好比律师对象(从中可以得出委托是一个类,因为只有类才有对象的概念,从而也体现了C#是面向对象的语言)。
介绍完生活中委托是个什么后,现在就看看C#中的委托怎样和生活中的对象联系起来的,C#中的委托相当于C++中的函数指针(如果之前学过C++就知道函数指针是个什么概念的了),函数指针是用指针获取一个函数的入口地址,然后通过这个指针来实现对函数的操作。C#中的委托相当于C++中的函数指针,也就说两者是有区别的:委托是面向对象的,类型安全的,是引用类型(开始就说了委托是个类),所以在使用委托时首先要 定义——>声明——>实例化——>作为参数传递给方法——>使用委托。下面就具体看下如何使用委托的:
一、定义:delegate void Mydelegate(type1 para1,type2 para2);
二、声明: Mydelegate d;
(很多类型声明和定义融合在一起了eg:public int i;)
三、实例化:d = new Mydelegate(obj.InstanceMethod);
(把一个方法传递给委托的构造器),前面三步就好比构造一个律师对象,方法InstanceMethod好比是当事人,现在当事人找到了自己的委托律师。
四、作为参数传递给方法:MyMethod(d);(委托实现把方法作为参数传入到另一个方法,委托就是一个包装方法的对象)这一点是更加体现了编程思想的地方,把操作都抽象化。
五、在方法中使用委托。MyMethod方法好比是法官,MyMethod方法先调用委托,委托在调用方法InstanceMethod,这个过程就如法官向律师问话,然后律师之前肯定向当事人了解了案件的情况。C#委托中好比是律师,真真诉说案情的是当事人(真真被调用的是实例方法InstanceMethod)
MyMethod方法的定义如下:
1 private void MyMethod(Mydelegate mydelegate) 2 { 4 // 使用委托 5 mydelegate(arg1,arg2); 7 }
二、C#中为什么要使用委托的?
相信经过上面的介绍,大家应该对委托不再陌生了吧,然而我们为什么需要委托的,好好地为什么要实例化中间这个对象的,为什么不直接在MyMethod方法里面调用InstanceMethod方法的,这样不是自找麻烦的吗?为了大家可以更好的明白为什么要使用委托,下面通过一个Window Form的 ”文字抄写员“ 程序要解释下为什么。
程序实现的功能是:在下方文本框输入文字,勾选“书写到”组合框中的“文本区1”或“文本区2”复选框后点击“开始”按钮,程序会自动将文本框中的文字”抄写“到对应的文本区中去。程序界面如下:
传统的实现代码为:
1 namespace 文字抄写员 2 { 3 4 public partial class Form1 : Form 5 { 6 public Form1() 7 { 8 InitializeComponent(); 9 } 10 private void button1_Click(object sender, EventArgs e) 11 { 12 if (checkBox1.Checked == true) 13 { 14 textBox1.Clear(); 15 textBox1.Refresh(); 16 // 调用方法WriteRichTextBox1想文本区1写入文字 17 this.WriteTextBox1(); 18 textBox3.Focus(); 19 textBox3.SelectAll(); 20 } 21 if (checkBox2.Checked == true) 22 { 23 textBox2.Clear(); 24 textBox2.Refresh(); 25 // 调用方法WriteRichTextBox2想文本区2写入文字 26 this.WriteTextBox2(); 27 textBox3.Focus(); 28 textBox3.SelectAll(); 29 } 30 } 31 private void WriteTextBox1() 32 { 33 string data = textBox3.Text; 34 for (int i = 0; i < data.Length; i++) 35 { 36 textBox1.AppendText(data[i].ToString()); 37 //间歇延时 38 DateTime now = DateTime.Now; 39 while(now.AddSeconds(1)>DateTime.Now) 40 { 41 } 42 } 43 } 44 private void WriteTextBox2() 45 { 46 string data = textBox3.Text; 47 for (int i = 0; i < data.Length; i++) 48 { 49 textBox2.AppendText(data[i].ToString()); 50 //间歇延时 51 DateTime now = DateTime.Now; 52 while (now.AddSeconds(1) > DateTime.Now) 53 { 54 } 55 } 56 } 57 } 58 }
然而我们从代码中会发现WriteTextBox1()方法和WriteTextBox2()只有一行代码不一样的( textBox1.AppendText(data[i].ToString()); 和 textBox2.AppendText(data[i].ToString());),其他都完全一样,而这条语句的差别就在于向其中写入文本的控件对象不一样,一个是TextBox1和TextBox2,现在这样代码是实现了功能,但是我们试想下,如果要实现一个写入的文本框不止2个,而是好几十个甚至更多,那么不久要写出同样多数量的用于写入文本区的方法了吗?(那把要写的对象也就是TextBox提取出来,作为参数不就可以了吗?可是这样的话,如果执行的不是写操作,而是擦出操作呢,对象不同,操作也不同呢??因为需要把“操作”提取出来!也就是委托!操作都抽象!这就是面向对象的编程思想...)这样就不得不写重复的代码,导致代码的可读性就差,这样写代码也就是面向过程的一个编程方式,因为函数是对操作过程的一个封装,要解决这个问题,自然我们就想到面向对象编程,此时我们就会想到把变化的部分封装起来,然后再把封装的对象作为一个对象传递给方法的参数的(这个思想也是一种设计模式——策略模式,关于设计模式系列会在后面也会给出的),下面就利用委托来重新实现下这个程序:
1 namespace 文字抄写员 2 { 3 4 public partial class Form1 : Form 5 { 6 7 // 定义委托 8 9 private delegate void WriteTextBox(char ch); 10 11 12 // 声明委托 13 14 private WriteTextBox writeTextBox; 15 16 public Form1() 17 18 { 19 20 InitializeComponent(); 21 22 } 23 24 private void button1_Click(object sender, EventArgs e) 25 { 26 27 if (checkBox1.Checked == true) 28 { 29 30 textBox1.Clear(); 31 textBox1.Refresh(); 32 33 // 用一个函数实例化委托 34 writeTextBox = new WriteTextBox(WriteTextBox1); 35 36 // 委托被实例化后作为参数传递给统一的一个函数WriteText() 37 WriteText(writeTextBox); 38 39 textBox3.Focus(); 40 textBox3.SelectAll(); 41 42 } 43 if (checkBox2.Checked == true) 44 45 { 46 47 textBox2.Clear(); 48 textBox2.Refresh(); 49 50 // 用另一个函数实例化委托 51 writeTextBox = new WriteTextBox(WriteTextBox2); 52 53 // 作为参数 54 WriteText(writeTextBox); 55 56 textBox3.Focus(); 57 textBox3.SelectAll(); 58 } 59 } 60 61 //在这个函数中,委托作为一个参数被传递到这个方法中 62 private void WriteText(WriteTextBox writetextbox) 63 { 64 string data = textBox3.Text; 65 for (int i = 0; i < data.Length; i++) 66 { 67 68 // 使用委托 69 writetextbox(data[i]); 70 71 DateTime now = DateTime.Now; 72 73 while (now.AddSeconds(1) > DateTime.Now) 74 { 75 } 76 77 } 78 79 } 80 private void WriteTextBox1(char ch) 81 { 82 83 textBox1.AppendText(ch.ToString()); 84 85 } 86 87 private void WriteTextBox2(char ch) 88 89 { 90 91 textBox2.AppendText(ch.ToString()); 92 93 } 94 } 95 }
引入委托后实现的代码中,我们通过WriteText方法来向文本区写入内容,它所执行的只是抽象的”写文本“操作,至于究竟像那个文本框写入文字,对于编写WriteText方法的程序来说是不知道,委托writeTextBox就像一个接口一样(面向对象设计原则中有一个很重要的原则就是——针对接口编程,不针对实现编程 依赖倒转原则),屏蔽了操作对象的差别(方法到底是想向文本区1写入文本还是像文本区2写入文本,现在我方法里面不需要去关心,我只需要集中在实现”书写文本”这个操作,而不必纠结操作对象的选择)。
三、委托的作用到底是什么?——委托总结陈词
相信通过上面两部分大家也明白了委托是个什么东西以及C#中为什么要引入委托这个概念。现在就总结下引入委托后到底作用在那里的? 从上面的委托代码中可以发现,引入委托后,编程人员可以把方法的引用封装在委托对象中(把过程的调用转化为对象的调用,充分体现了委托加强了面向对象编程的思想。),然后把委托对象传递给需要引用方法的代码,这样在编译的过程中我们并不知道调用了哪个方法,这样一来,C#引入委托机制后,使得方法声明和方法实现的分离,充分体现了面向对象的编程思想。
委托对自己的总结:
我是一个特殊的类,我定义了方法的类型,(就像int定义了数字类型一样,当用一个方法实例化委托对象时,这个委托就代表一个方法,这个方法的类型就是委托类型),我可以将方法当做另一个方法的参数来进行传递,使得程序更容易扩展。