C# 委托

一、前言:每次看到委托和事件,心理面总是不自在,原因大家都懂,但是委托和事件在.NET FrameWork里面的应用非常的广泛,所以熟练的掌握委托和事件对一个.NET开发人员来说是十分重要的,所以花半天的时间来彻底的扫下盲点,争取能carry掉!

二、概述

1、作用:委托的作用是将方法作为参数传递给方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class Program
    {
        static void Main(string[] args)
        {   
            Program p=new Program();
            p.SayHello("张三");
        }
        public void SayHello(string name)
        {
            ChineseSayHello(name);
        }

        private void ChineseSayHello(string name)
        {
            Console.WriteLine("你好:{0}",name);
        }
    }
}

上面的代码主要是一个打招呼程序,当你给SayHello()方法输入参数姓名是,该程序就会吊用中文式的SayHello的方法,但是有一个问题,现在这个程序需要国际化,需要填加其他国家的SayHello()方法,如何美国的,那么这个时候应该加一个language参数,来区分不同的国家,下面是改进后的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class SayHello1
    {
        static void Main(string[] args) {
            SayHello1 s1 = new SayHello1();
            s1.SayHello("张三", Country.Chinese);
            s1.SayHello("张三", Country.America);
        }
        public void SayHello(string _name, Country _country)
        {
            switch (_country) {
                case Country.America: EnglishSayHello(_name); break;
                case Country.Chinese: ChineseSayHello(_name); break;
                default: ChineseSayHello(_name); break;
            }
        }
        private void EnglishSayHello(string _name) {
            Console.WriteLine("Hello {0}", _name);
        }
        private void ChineseSayHello(string _name)
        {
            Console.WriteLine("你好 {0}", _name);
        }
    }
    public enum Country { 

        //中国
        Chinese,
        //美国
        America
    }
}

上面的代码通过枚举和switch判断,解决了国际化的问题,但是这样的代码耦合度太高,试想一下,当我们每加一个国家,SayHello()方法就要改变,这是不妥的,话句话说,就是假设我们加一个韩国的SayHello()的方法,在设计层面上来看它仅仅是加了一个韩国的SayHello的方法,但是从代码和角度看,他改变的是整个SayHello()方法的结构,这是不妥的,中国和美国的SayHello方法不应该因为额外加了一个韩国的SayHello方法所出现的安全问题买单。所以这种方法虽然解决了问题,但是产生了不必要的问题,代码的扩展性很差。

下面通过委托来解决上面的问题

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    //定义一个委托EventHander是微软定义委托的一种标准委托的名字+EventHander
    //定义了一个无返回值,参数为name的委托
    //注意委托定义的位置和string,delegate,SayHello2的位置是一样的,说明委托也应该是个类型,或者说类
    //但是委托的声明方式,却和类不一样。实际上委托最后确实会被编译成一个类.应为delegate是个类,所以任何可以声明类的地方都可以声明委托

    public delegate void SayHelloEventHander(string name);
    class SayHello2
    {
        private static void Main(string[] args) {
            SayHello2 s2 = new SayHello2();
            s2.SayHello("张三", s2.ChineseSayHello);
            s2.SayHello("zhangsan", s2.EnglishSayHello);
        }
        public void SayHello(string _name, SayHelloEventHander _sayHello) {
            _sayHello(_name);     
        }
        private void EnglishSayHello(string _name) {
            Console.WriteLine("hello:{0}", _name);
        }
        private void ChineseSayHello(string _name)
        {
            Console.WriteLine("你好:{0}", _name);
        }
    }
}

总结:

<1>委托是一个类,任何可以声明类的地方,都可以声明委托.

<2>委托可以定义方法的类型和返回值

<3>通过使用委托,将方法作为参数传递给方法的方式,减少了程序中if else和switch语句出现的次数,增加了程序的可扩展性

2、委托也是一种数据类型

(1)、上面的代码中提到了,可以定义类的地方,就可以定义委托,那么我们就可以推断出,委托实际上也是一种数据类型,我们也可以像申明类一样的方式来申明委托.并调用它;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    public class SayHello3
    {
        public delegate void SayHelloEventHandler(string _name);
        static void Main(string[] args)
        {
            SayHello3 s3 = new SayHello3();
            SayHelloEventHandler s1, s2;//定义了两个委托实例
            //为实例赋值
            s1 = s3.ChineseSayHello;
            s2 = s3.EnglishSayHello;
            s3.SayHello("张三", s1);
            s3.SayHello("张三", s2);
        }
        public void SayHello(string _name, SayHelloEventHandler sayHello)
        {
            sayHello(_name);
        }
        private void ChineseSayHello(string _name)
        {
            Console.WriteLine("你好:{0}", _name);
        }
        private void EnglishSayHello(string _name)
        {
            Console.WriteLine("hello:{0}", _name);
        }
    }
}

(2)、将多个方法绑定给同一个委托

这是委托的一个特性:可以将多个方法赋给同一个委托,或者将多个方法绑定给同一个委托,当调用这个委托的时候,讲一次吊用该委托所绑定的方法;代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class SayHello5
    {
        public delegate void SayHelloEventHandler(string _name);
        static void Main(string[] args) {
            SayHelloEventHandler sr1;
            SayHello5 s5 = new SayHello5();
            //如果需要给SayHelloEventHandler绑定多个方法,必须先给sr1赋初值,在做+=操作,否则编译器会报错
            sr1 = s5.EnglishSayHello;
            sr1 += s5.ChineseSayHello;
            sr1 -= s5.EnglishSayHello;
            sr1 += s5.EnglishSayHello;
            s5.SayHello("张三", sr1);
        }
        public void SayHello(string _name,SayHelloEventHandler sayHello) {
            sayHello(_name);
        }
        private void ChineseSayHello(string _name)
        {
            Console.WriteLine("你好:{0}", _name);
        }

        private void EnglishSayHello(string _name)
        {
            Console.WriteLine("hello:{0}", _name);
        }
    }
}

(3)上面代码中提到了当我们需要给一个委托绑定多个方法是,必须先给第一个赋初值在做+=运算,如果直接+=编译器则会报错。所以这个问题引申出第二种委托定义的方式,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class Thread4
    {
        public delegate void SayHelloEventHandler(string _name);
        static void Main(string[] args)
        {
            //前面说过委托也是一种类型,他可以向类一样的创建实例
            SayHelloEventHander sh = new SayHelloEventHander(ChineseSayHello);//创建了一个SayHelloEventHandler的实例,并给SayHelloEventHandler构造函数赋了初值ChineseSayHello
            //然后就可以做+=操作,做后续绑定工作
            sh += EnglishHello;
        }

        private static void ChineseSayHello(string name)
        {
            
        }
        private static void EnglishHello(string name)
        {

        }

    }
}

(4)利用面向对象的方法,封装代码,给上面的代码解耦

分析上面的代码:发现SayHello()这个方法是不变的,那我们就把它封装起来,我们把SayHello的方法封装成SayHelloManager类,外部程序直接通过SayHelloManager类来访问并设置对应的SayHello()方法和参数。

using System;
namespace Delegate
{
    //定义一个无返回值,但是有一个name参数的委托
    public delegate void SayHelloEventHandler(string _name);
    class SayHello6
    {
        static void Main(string[] args)
        {   
            //第一种方法
            SayHelloManger sm = new SayHelloManger();
            sm.SayHelloEventHandler = ChineseSayHello;
            sm.SayHelloEventHandler += EnglishSayHello;
            sm.SayHello("张三", sm.SayHelloEventHandler);


            //第二种调用方式
            SayHelloManger sm1 = new SayHelloManger("张三", ChineseSayHello);
            sm1.SayHello();
            SayHelloManger sm2 = new SayHelloManger("张三",EnglishSayHello);
            sm2.SayHello();
        }
        static void ChineseSayHello(string _name){
            Console.WriteLine("你好:{0}", _name);
          }
        static void EnglishSayHello(string _name){
            Console.WriteLine("Hello:{0}", _name);
          }
    }
    //定义一个SayHelloManager类,将所有SayHello()方法需要的参数设置成实例,并给外部调用
    public class SayHelloManger
    {
        public string _name;
        public SayHelloEventHandler _sayHelloEventHandler;
        public SayHelloEventHandler SayHelloEventHandler
        {
            get { return _sayHelloEventHandler; }
            set { _sayHelloEventHandler = value; }
        }
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public SayHelloManger()
        {
        }
        public SayHelloManger(string _name, SayHelloEventHandler handler)
        {
            this.SayHelloEventHandler = handler;
            this.Name = _name;
        }
        public void SayHello()
        {
            this.SayHelloEventHandler(Name);
        }
        public void SayHello(string _name,SayHelloEventHandler SayHello)
        {
            SayHello(_name);
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

C# string byte[] Base64 常用互相转换

http://www.cnblogs.com/zxx193/p/3605238.html?utm_source=tuicool

2201
来自专栏GreenLeaves

Linq基础知识小记一

1、LINQ(语言级集成查询)的作用就是提供一种统一且对称的方式,让程序员在广义的数据上获取和操作数据。广义的数据包括XML文档数据、元数据、System.Da...

1978
来自专栏算法修养

CodeForces 666B World Tour(spfa+枚举)

B. World Tour time limit per test 5 seconds memory limit per test 512 mega...

3394
来自专栏逸鹏说道

Python3 与 C# 基础语法对比(String专栏-新排版)

在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master

1252
来自专栏我和未来有约会

(保存)C#基础概念二十五问

注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!     当初学 C# 时是找个人大概问了一下数据类型和分支语句就开始做项目了...

2128
来自专栏菩提树下的杨过

利用Linq对集合元素合并、去重复处理

今天写代码时,需要对一个数组对象中按一定规则合并、去重处理,不想再毫无新意手动写For循环遍历(天天写一样的代码很没劲),于是依旧linq,发现真心方便: us...

2719
来自专栏小樱的经验随笔

Codeforces 842A Kirill And The Game【暴力,水】

A. Kirill And The Game time limit per test:2 seconds memory limit per test:256 m...

2907
来自专栏javathings

Java 中 Comparable 和 Comparator 有何不同?

Comparable 和 Comparator 都有比较的含义,那么他们之前有什么区别?

2514
来自专栏机器学习算法与Python学习

pyhton-----break语句

Python break语句,就像在C语言中,打破了最小封闭for或while循环。break语句用来终止循环语句,即循环条件没有False条件或者序列还没被完...

2985
来自专栏java 成神之路

CompletableFuture 使用详解

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下...

6793

扫码关注云+社区

领取腾讯云代金券