[C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托

转自http://www.cnblogs.com/zhili/archive/2012/10/22/Delegate.html

引言:

对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以有了这个系列,希望通过这个系列让朋友对C#的基础知识理解能够更进一步。然而委托又是C#基础知识中比较重要的一点,基本上后面的特性都和委托有点关系,所以这里就和大家先说说委托,为什么我们需要委托。

一、C#委托是什么的?

在正式介绍委托之前,我想下看看生活中委托的例子——生活中,如果如果我们需要打官司,在法庭上是由律师为我们辩护的,然而律师真真执行的是当事人的陈词,这时候律师就是一个委托对象,当事人委托律师这个对象去帮自己辩护。这就是我们生活中委托的例子的。然而C#中委托的概念也就好比律师对象(从中可以得出委托是一个类,,因为只有类才有对象的概念,从而也体现了C#是面向对象的语言)。

介绍完生活中委托是个什么后,现在就看看C#中的委托怎样和生活中的对象联系起来的,C#中的委托相当于C++中的函数指针(如果之前学过C++就知道函数指针是个什么概念的了),函数指针是用指针获取一个函数的入口地址,然后通过这个指针来实现对函数的操作。C#中的委托相当于C++中的函数指针,也就说两者是有区别的:委托是面向对象的,类型安全的,是引用类型(开始就说了委托是个类),所以在使用委托时首先要 定义——>声明——>实例化——>作为参数传递给方法——>使用委托。下面就具体看下如何使用委托的:

一、定义:delegate void Mydelegate(type1 para1,type2 para2);

二、声明: Mydelegate d;

三、实例化:d =new Mydelegate(obj.InstanceMethod);(把一个方法传递给委托的构造器),前面三步就好比构造一个律师对象,方法InstanceMethod好比是当事人

四、作为参数传递给方法:MyMethod(d);(委托实现把方法作为参数传入到另一个方法,委托就是一个包装方法的对象)

五、在方法中使用委托。MyMethod方法好比是法官,MyMethod方法先调用委托,委托在调用方法InstanceMethod,这个过程就如法官向律师问话,然后律师之前肯定向当事人了解了案件的情况。C#委托中好比是律师,真真诉说案情的是当事人(真真被调用的是实例方法InstanceMethod)

MyMethod方法的定义如下:

private void MyMethod(Mydelegate mydelegate)

{
    // 使用委托
    mydelegat(arg1,arg2);

}

二、C#中为什么要使用委托的?
相信经过上面的介绍,大家应该对委托不再陌生了吧,然而我们为什么需要委托的,好好地为什么要实例化中间这个对象的,为什么不直接在MyMethod方法里面调用InstanceMethod方法的,这样不是自找麻烦的吗?为了大家可以更好的明白为什么要使用委托,下面通过一个Window Form的 ”文字抄写员“ 程序要解释下为什么。

程序实现的功能是:在下方文本框输入文字,勾选“书写到”组合框中的“文本区1”或“文本区2”复选框后点击“开始”按钮,程序会自动将文本框中的文字”抄写“到对应的文本区中去。程序界面如下:

传统的实现代码为:

namespace 文字抄写员
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                textBox1.Clear();
                textBox1.Refresh();
                // 调用方法WriteRichTextBox1想文本区1写入文字
                this.WriteTextBox1();
                textBox3.Focus();
                textBox3.SelectAll();
            }
            if (checkBox2.Checked == true)
            {
                textBox2.Clear();
                textBox2.Refresh();
                // 调用方法WriteRichTextBox2想文本区2写入文字
                this.WriteTextBox2();
                textBox3.Focus();
                textBox3.SelectAll();
            }
        }

        private void WriteTextBox1()
        {
            string data = textBox3.Text;
            for (int i = 0; i < data.Length; i++)
            {
                textBox1.AppendText(data[i].ToString());
                //间歇延时
                DateTime now = DateTime.Now;
                while(now.AddSeconds(1)>DateTime.Now)
                { }
            }
        }

        private void WriteTextBox2()
        {
            string data = textBox3.Text;
            for (int i = 0; i < data.Length; i++)
            {
                textBox2.AppendText(data[i].ToString());
                //间歇延时
                DateTime now = DateTime.Now;
                while (now.AddSeconds(1) > DateTime.Now)
                {  }
            }
        }
    }
}

然而我们从代码中会发现WriteTextBox1()方法和WriteTextBox2()只有一行代码不一样的( textBox1.AppendText(data[i].ToString()); 和 textBox2.AppendText(data[i].ToString());),其他都完全一样,而这条语句的差别就在于向其中写入文本的控件对象不一样,一个是TextBox1和TextBox2,现在这样代码是实现了功能,带式我们试想下,如果要实现一个写入的文本框不止2个,而是好几十个甚至更多,那么不久要写出同样多数量的用于写入文本区的方法了吗?这样就不得不写重复的代码,导致代码的可读性就差,这样写代码也就是面向过程的一个编程方式,因为函数是对操作过程的一个封装,要解决这个问题,自然我们就想到面向对象 编程,此时我们就会想到把变化的部分封装起来,然后再把封装的对象作为一个对象传递给方法的参数的(这个思想也是一种设计模式——策略模式,关于设计模式系列会在后面也会给出的),下面就利用委托来重新实现下这个程序:

namespace 文字抄写员
{
    public partial class Form1 : Form
    {
        // 定义委托
        private delegate void WriteTextBox(char ch);
        // 声明委托
        private WriteTextBox writeTextBox;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                textBox1.Clear();
                textBox1.Refresh();
                // 实例化委托
                writeTextBox = new WriteTextBox(WriteTextBox1);
                // 作为参数
                WriteText(writeTextBox);

                textBox3.Focus();
                textBox3.SelectAll();
            }
            if (checkBox2.Checked == true)
            {
                textBox2.Clear();
                textBox2.Refresh();
                // 实例化委托
                writeTextBox = new WriteTextBox(WriteTextBox2);
                // 作为参数
                WriteText(writeTextBox);

                textBox3.Focus();
                textBox3.SelectAll();
            }
        }

        private void WriteText(WriteTextBox writetextbox)
        {
            string data = textBox3.Text;
            for (int i = 0; i < data.Length; i++)
            {
                // 使用委托
                writetextbox(data[i]);
                DateTime now = DateTime.Now;
                while (now.AddSeconds(1) > DateTime.Now)
                { }
            }
        }

        private void WriteTextBox1(char ch)
        {
            textBox1.AppendText(ch.ToString());
        }
        private void WriteTextBox2(char ch)
        {
            textBox2.AppendText(ch.ToString());
        }
    }
}

引入委托后实现的代码中,我们通过WriteText方法来向文本区写入内容,它所执行的只是抽象的”写文本“操作,至于究竟像那个文本框写入文字,对于编写WriteText方法的程序来说是不知道,委托writeTextBox就像一个接口一样(面向对象设计原则中有一个很重要的原则就是——针对接口编程,不针对实现编程),屏蔽了操作对象的差别(方法到底是想向文本区1写入文本还是像文本区2写入文本,现在我方法里面不需要去关心,我只需要集中在实现”书写文本”这个操作,而不必纠结操作对象的选择)。

三、委托的作用到底是什么?——委托总结陈词

相信通过上面两部分大家也明白了委托是个什么东西以及C#中为什么要引入委托这个概念。现在就总结下引入委托后到底作用在那里的? 从上面的委托代码中可以发现,引入委托后,编程人员可以把方法的引用封装在委托对象中(把过程的调用转化为对象的调用,充分体现了委托加强了面向对象编程的思想。),然后把委托对象传递给需要引用方法的代码,这样在编译的过程中我们并不知道调用了哪个方法,这样一来,C#引入委托机制后,使得方法声明和方法实现的分离,充分体现了面向对象的编程思想。

委托对自己的总结:

我是一个特殊的类,我定义了方法的类型,(就像int定义了数字类型一样,当用一个方法实例化委托对象时,这个委托就代表一个方法,这个方法的类型就是委托类型),我可以将方法当做另一个方法的参数来进行传递,使得程序更容易扩展

四、小结

写到这里本专题介绍的内容也结束了,在本专题中有些地方提到了一些设计模式的知识的,如果有朋友对设计模式还没有开始学习的话,建议大家都去学习下的,并且我也会在后面的系列中向大家分享下我的理解的。对于本系列的下一专题将和大家分享下我理解的事件到底是个什么样的概念。最后希望本专题可以让大家进一步理解委托。

时间: 2024-11-11 07:24:53

[C# 基础知识系列]专题一:深入解析委托——C#中为什么要引入委托的相关文章

[C#基础知识系列]专题十:全面解析可空类型[转]

原文链接 主要内容: 1:空合并操作符(?? 操作符) ??操作符也就是"空合并操作符",它代表的意思是两个操作数,如果左边的数不为null时,就返回左边的数,如果左边的数为null,就返回右边的数,这个操作符可以用于可空类型,也可以用于引用类型,但是不能用于值类型(之所以不能应用值类型(这里除了可空类型),因为??运算符要对左边的数与null进行比较,然而值类型,不能与null类型比较,所以就不支持??运算符),下面用一个例子来掩饰下??运算符的使用(??这个运算符可以方便我们设置默

[C# 基础知识系列]专题十六:Linq介绍

转自http://www.cnblogs.com/zhili/archive/2012/12/24/Linq.html 本专题概要: Linq是什么 使用Linq的好处在哪里 Linq的实际操作例子——使用Linq遍历文件目录 小结 引言: 终于到了C# 3中最重要特性的介绍了,可以说之前所有介绍的特性都是为了Linq而做准备的,然而要想深入理解Linq并不是这个专题可以介绍完的,所以我打算这个专题将对Linq做一个简单的介绍,对于Linq的深入理解我将会后面单独作为一个系列要和大家分享下. 一

[C# 基础知识系列]专题四:事件揭秘

转自http://www.cnblogs.com/zhili/archive/2012/10/27/Event.html 引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到“事件”这个概念的,尤其是写UI的时候,当我们点击一个按钮后VS就会自动帮我们生成一些后台的代码,然后我们就只需要在Click方法里面写代码就可以,所以可能有些刚接触C#的朋友就觉得这样很理所当然的,也没有去思考这是为什么的,为什么点击下事件就会触发我们在Click方法里面写的代码呢?事件到底扮演

线程基础知识系列(三)线程的同步

本文是系列的第三篇,前面2篇,主要是针对单个线程如何管理,启动等,没有过多涉及多个线程是如何协同工作的. 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本文的主要内容 何谓线程安全? 何谓共享可变变量? 认识synchronized关键字 认识Lock synchronized vs Lock 1.何谓线程安全 多线程是把双刃剑,带

线程基础知识系列(二)线程的管理

本篇是线程基础知识系列的第二篇,主要简单江夏线程管理相关知识点. 线程基础知识系列(一)线程的创建和启动:说明了线程的2种创建和启动,join(),daemon线程,Callable 任务. 本文的主要内容 线程的状态 线程的优先级 sleep vs wait 线程的流程控制 Interrupt yield让出你的CPU 1.线程的状态 以<线程基础知识系列(一)线程的创建和启动>这张图,是程序的运行时线程信息截图.有main线程,user Threads,daemon Threads.现在咱

线程基础知识系列(四)线程的同步2 线程通信和Condition变量

本文是系列的第四篇. 线程基础知识系列(三)线程的同步  :同步控制,锁及synchronized 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 第三篇文章,重点阐述了如何使用锁和同步块对线程间共享可变变量保护,保证只有一个线程可以进入临界区.其实,没有过多的涉及另一个重要的同步概念:线程协作.第三篇中涉及的线程间并没有有效的协调.

线程基础知识系列(五)认识volatile

线程基础知识系列(四)线程的同步2  :线程的notify-wait通信机制,以及Condition条件变量 线程基础知识系列(三)线程的同步  :同步控制,锁及synchronized 线程基础知识系列(二)线程的管理 :线程的状态,控制,休眠,Interrupt,yield等 线程基础知识系列(一)线程的创建和启动  :线程的创建和启动,join(),daemon线程,Callable任务. 本篇文章主要讨论的关键字是volatile. volatile使用场景 volatile介绍 vol

基础知识系列?闲言

以前总是觉得... 写一些基础知识的博客·不就是抄书抄书嘛... 东抄西拷-- 并且发现基础知识就是这些内容嘛··· A写的和B写的都差不多·只是细节和深度不太一样··· 1 也曾问自己什么时候能写一点有深度的博客·装下X... 2 刚开始学习也是啃书...不过那时候确实没有经验吧. 3 啃书很吃力的说... 4 5 从中也知道了,人看书也要分境界的: 6 有些话·你读了不一定懂... 7 因为你根本没经历过(也说明了边读书边撸代码的重要性,需要自己去体会)··· 1 总之基础知识还是要好好慢慢

Java基础知识强化103:JSON解析框架汇总

1.Gson Gson是Google提供的一个能够将Java对象转换成相应JSON表达形式的一个开源Java类库,当然用Gson也能将JSON字符串转换成与之等价的Java对象.Gson对于任何Java对象都有效,包括那些预先存在没有源代码的对象. 现在已经有一些能将Java对象转换成JSON的开源项目了.但是大多数项目都要求你在类文件中加入Java注解,而当你无法改动源代码的时候这是无法做到的.并且它们也不支持Java泛型.但是Gson却将这两点作为自己非常重要的设计目标. 目标 使用toJs