C#中委托的基础入门与实现方法

目录
  • 前言
  • 关于委托
  • 委托的实现
    • 一、基本实现方式
    • 二、使用委托时的一些特殊方式
      • 1、委托实例对象的创建多元化:
      • 2、事件绑定的多种方式
    • 三、委托的几种特殊实现方式
    • 1,使用Action方法
      • 2,使用Func方法
    • 四、委托的一些特殊小知识
      • 1、委托闭包的产生
      • 2,关于事件
  • 总结

前言

似乎委托对于C#而言是一种高级属性,但是我依旧希望你就算第一次看我的文章,也能有很大的收获。

所以本博客的语言描述尽量简单易懂,知识点也是面向初入门对于委托不了解的学习者的。当然如果有幸有大佬发现文章的错误点,也欢迎留言指出!

关于委托

关于委托的介绍主要来源于C#文档:委托概述(本文章优势在于去掉一些不必要的细节,对于初学者而言简单高效)

委托的定义主要是下面几个方面:

  • 委托是一种引用类型:表示对具有特定参数列表和返回类型的方法的引用
  • 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法

委托本质上来讲就是将方法作为参数传递给其他方法的一种实现方式,当然开发者也可以直接去调用方法。但是当一个项目扩展到足够大时,这种直接调用的方式就会很复杂,难以维护。而委托不会,可以很方便的进行后期的扩展开发。只需要将自己的方法传入已经写好的对应的委托即可。而不需要再在大量的代码中找到调用处写入自己的方法。

关于委托的一些特点是(暂时不了解没有关系):

  • 委托类似于 C++ 函数指针,但委托完全面向对象,不像 C++ 指针会记住函数,委托会同时封装对象实例和方法。
  • 委托允许将方法作为参数进行传递。
  • 委托可用于定义回调方法。
  • 委托可以链接在一起;例如,可以对一个事件调用多个方法。
  • 方法不必与委托类型完全匹配。 有关详细信息,请参阅使用委托中的变体
  • 使用Lambda 表达式可以更简练地编写内联代码块。 Lambda表达式(在某些上下文中)可编译为委托类型。 若要详细了解lambda 表达式,请参阅 lambda 表达式

如果对于一个初学者,你可以简单的理解,委托就是一个更高级的调用方法的方式,而你要学习的,就是这种方式的实现方法,然后后期再慢慢理解更多的细节。

委托的实现

一、基本实现方式

前面也说,委托是一个引用类型,要使用委托,肯定就需要对其进行定义并创建一个委托对象,下面使用一个带有一个参数的案例来理解委托

public class DemoDelegate
{
    	//T1:
        delegate void TestDel(string s);
        TestDel Del;
        //T4:
        static void Main(string[] args)
        {
            DemoDelegate demo = new DemoDelegate();
            demo.CreateDelObject();
            demo.Del?.Invoke("你们好");
        }
    	//T3:
        public void CreateDelObject()
        {
            Del += TestEventOne;
            Del += TestEventTwo;
        }
    	//T2:
        public void TestEventOne(string str)
        {
            Console.WriteLine(str);
        }
        public void TestEventTwo(string str)
        {
            Console.WriteLine(str);
        }
    }

如果你是刚刚接触委托这个概念,可能对于这些代码的含义不是特别了解,没关系,你可以根据注释的顺序来看代码并理解委托的实现机制:

  • T1:通过delegate关键字定义一个委托类型TestDel,并创建一个实例Del
  • T2: 也很好理解,创建两个测试方法,可以执行输出
  • T3: 是委托的关键,为刚刚创建的委托来添加事件方法
  • T4: 执行委托实例Del,可以简单理解为执行该实例绑定的所有事件方法

当然有一些语法是比较独特的,比如说+=这样的语法,就是一种为委托添加事件方法的方法,而对于执行委托语句demo.Del?.Invoke("你们好");中Invoke()为执行该委托对象内方法的API,?则可以在Del委托对象为空时,系统不报错

关于?的具体含义,可查阅:

可为空引用类型

其实委托主要是有这简单的四步来实现了,通过这个案例,更加明显的体现出委托将一系列方法作为参数来让其他方法去调用的特点。

二、使用委托时的一些特殊方式

通过上面的案例,可以看出对于委托的实现是很简单的,但是C#还是为我们提供了很多更加间接或者集成的用法,具体有:

1、委托实例对象的创建多元化:

创建委托类型的多种方式:

  • 直接使用New来创建一个对象,但是注意,在New时需要绑定直接添加一个事件方法,不然会报错(这也是与其他引用类型不同的地方)
  • 使用直接赋值的方式创建
  • 定义方法名,后期添加事件方法时自动实例(上面的例子)

关于具体的实现代码:

public class DemoDelegate
{
    delegate void TestDel(string str);
    //第一种:New 的同时绑定方法
    TestDel DelOne = new TestDel(TestEventOne);
    //第二种
    TestDel DelTwo = TestEventOne;

    static void TestEventOne(string str)
    {
        Console.WriteLine(str);
    }
}

注意,关于第三种后期绑定有一种现象值得留意,在使用后期绑定时,如果你是创建一个成员变量(全局变量)委托类型,后期绑定可以直接使用+=来增加委托绑定的方法,而你如果是创建一个局部变量的委托,需要先通过=来添加一个方法后,才能使用+=来增加方法,不然就会报空,如图:

出现这种情况的原因在于成员变量与局部变量之间的区别,如果想要了解更多,可以执行百度,这边列出两者在本案例中的区别:

成员变量:有默认初始化值局部变量:没有默认初始化值,必须定义,赋值,然后才能使用。

2、事件绑定的多种方式

对于事件的绑定,可以使用的方式有很多,在不同的情况下不同的方式也有不同的优势与局限性,可以根据自己的需求进行自行选择

  • 使用方法名通过+=来添加方法,可以通过-=来删除方法
  • 使用匿名方法
  • 使用Lambda表达式

关于第一种方法,已经在上面表示的很清楚。

关于社会的进步与发展,某一方面来讲,是由于人类的懒来驱动的,C#开发人员可能觉得第一种方式太复杂了,于是就出现匿名函数的脚本,先通过代码来看一下其实现方式:

public class DemoDelegate
{
    delegate void TestDel(string str);
    TestDel Del;
    static void Main(string[] args)
    {
        DemoDelegate demo = new DemoDelegate();
        //匿名方法使用方式演示
        demo.Del = delegate (string str)
        {
            Console.WriteLine("这是一个匿名方法的测试");
        };

    }
}

通过上面的代码可以看出,通过匿名方式使得我们不需要重新定义方法来进行绑定,只需要通过委托关键字,而省去数据类型修饰符、方法签名等结构

匿名方法定义(菜鸟教程):

匿名方法提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。在匿名方法中不需要指定返回类型,它是从方法主体内的 return 语句推断的()



而Lambda 表达式更加极致,将能省掉的东西全部省掉,使得最终的表达式极其的简洁:

    public class DemoDelegate
    {
        delegate void TestDel(string str);
        TestDel Del;
        static void Main(string[] args)
        {
            DemoDelegate demo = new DemoDelegate();
       		//lambda表达式,括号内为参数变量名,如果没有直接()
            demo.Del += (str)=>
            {
                Console.WriteLine(str);
            };

        }
    }

可以通过脚本看出,Lambda 表达式对于语法的节省到达了极致,去掉了所有的修饰符,包括传入的参数的数据类型修饰符,只需要一个变量名即可,而一个委托如果没有参数,直接使用()即可,简单到极致

注意(关于()的应用):

如果Lambda 无参数,则必须要有()
有一个参数可以去掉(),直接+= str=>{};
如果有多个参数,也必须要有()

三、委托的几种特殊实现方式

除了使用delegate关键字来实现委托外,C#还提供了几种升级版的集成化的使用方式,比如Action和Func方法等等。但是注意,高的集成化往往意味着低的适配性。所以对于下面介绍几种方式他们往往有一定的使用限制,不如delegate来的灵活,不过对于特定场景更加的简单快捷

1,使用Action方法

在开始介绍使用方式之前,先说明一下其使用特殊

  • 对于没有返回值的委托,简单的理解就是不需要return(这种想法是错误的,但是好理解)

其实很简单的对不对,其脚本实现更加简单:

//无参数的Action委托对象的定义
Action<> actDemoOne;

//带参数的Action委托对象的定义,参数最多十六个
Action<int> actDemoTwo;

除了定义不同外,创建的委托实例对于方法的绑定与执行与标准的委托相同,其实Action本来就是通过delegate来定义的一个委托类型,但是这个定义是C#系统进行定义的。

在代码中我们很容易找到这句定义

而带参数的类型与上面类似

这样我们可以直接使用C#提供定义好的委托类型来创建我们的委托实例,这种方式的优势就是代码结构简洁好用。不过限制就是只能绑定没有返回值的方法

2,使用Func方法

其实Func的用法是与Action相反互补的,其主要的特点是有返回值,为了突出,依旧列出来

  • 主要用于没有参数的委托类型,且必须有返回值
  • 但是同时是可以有参数的,并不是与Action完全相反

通过代码来表述这一特点:

// int为该委托绑定方法的返回值类型,注意必须要有返回值
Func<int> funDemo;

//绑定与执行与标准委托相同,来复习一下
public class DemoDelegate
{
        static void Main(string[] args)
        {
             DemoDelegate demo = new DemoDelegate();

            Func<int> funDemo;
            //为委托添加方法
            funDemo = demo.TestEventOne;
            //执行委托
            funDemo?.Invoke();
            //依旧可以使用Lambda表达式
            funDemo += () =>
            {

                return 0;
            };

            /*-----------带参数的Func用法--------*/
            Func<string,int> funDemoTwo;
            funDemoTwo = demo.TestEventTwo;
        }

        public int TestEventOne()
        {
            return 0;
        }

    	public int TestEventTwo(string str)
        {
            Console.WriteLine(str);
            return 0;
        }
}

上面的代码将两种情况放在一起解释的,可以分开逻辑进行理解,但是重要的是要理解带参数的Func的用法,我们可以看到Func<string,int>有两个数据类型的写入,前面一个就是传入参数的类型,而后面就是返回值类型,一定要区分开来

四、委托的一些特殊小知识

1、委托闭包的产生

在正式开始介绍闭包概念之前,先通过一个案例来发掘关于闭包产生的现象,你可以猜想一下下面的代码的输出结果:

    class DemoDelegate
    {
        delegate void TestDel();
        TestDel Del;
        static void Main(string[] args)
        {
            DemoDelegate demo = new DemoDelegate();
            for (int i = 0; i < 3; i++)
            {
                demo.Del+= () =>
                {
                    Console.WriteLine(i);
                };
            }
            demo.Del?.Invoke();
    	}
    }

如果根据正常的逻辑思路去判断,会很直接的判断得到的输出为:0、1、2,但是经过执行打印却发现最终的输出结果为:3、3、3(为啥不是2、2、2,思考一下++i与i++,或者往后看)

这样的结果确实很奇妙,但是如果认真思考,你可能会有一些小想法,是不是由于存储的是数据i地址呢,而导致最终结果相同呢。其实可以简单的理解成这个样子。

如果想要更加深入的了解闭包,希望下面的一些解释可以帮助你理解

闭包概念:

是一个函数与其他相关引用环境组合的实体,而在引用环境消失后,该函数会依旧保存从函数内引用的变量

根据上面的例子来翻译一下就是委托的代码块使用了代码块外的变量,而这个变量是Test()方法的局部变量,当Test()方法执行完毕后,这个局部变量本应该被销毁,但是由于闭包原因却保存其状态到内存中,来保证自己后续的使用。

因此,所有的委托方法需要的变量内存地址都指向了本应被销毁的局部变量i,最终的读取就是i最后的状态,也就是全是经过三次i++后的3

闭包需要注意的问题

如果使用必要,需要注意由于闭包而产生的内存泄露的问题,由于闭包是访问另一个函数中的变量,就会影响另一函数中的局部变量的内存回收。而一直占用在内存中。造成内存泄露的后果。

但是也有另外的观点讲闭包不会造成内存泄露。原因在于C#的垃圾回收是有相应的处理的。由于本人对于垃圾回收机制认识比较浅显,目前也做不出判断。还希望了解的人可以留言告知

2,关于事件

在C#中,也提出了事件这个概念,本质上来讲也是委托,但是是一个受限的委托

与灵活的委托不同,事件只能在定义的类内被调用,不过可以在其他类里面进行方法的绑定

事件的用法

在我们定义一个委托类型后,我们可以通过event创建一个事件实例:

通过上面的案例可以看到,使用event修饰委托实例后,只能够在定义类中执行委托的方法,而不能在其他类中去调用执行

总结

关于委托的内容还是挺多的,但是大多都是对于本质的东西进行的一些扩展,在学习初期,只需要掌握关于delegate的核心的用法即可,后续再慢慢的延申扩展

到此这篇关于C#中委托的基础入门与实现的文章就介绍到这了,更多相关C#委托基础入门内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c# 委托和事件实例学习

    Common.cs: 复制代码 代码如下: using System; using System.Collections.Generic; using System.Text; namespace DelegateAndEvent.App_Code { public class Common { //定义全局变量. public static string txt = ""; #region 定义方法 public string HelloCSharp(string name) { t

  • 深入理解C#中常见的委托

    一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的.关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微.我就不用多废话了.今天我要说的是C#中的三种委托方式:Func委托,Action委托,Predicate委托以及这三种委托的常见使用场景.Func,Action,Predicate全面解析首先来说明Func委托,通过MSDN我们可以了解到,Func委托有如下的5种类型: 复制代码 代码如下: (1) *delegate TRes

  • C#中自定义事件和委托实例

    在windows 编程中用到最多的就是控件的事件了,微软给我们很好的方式,把注意力放到事件执行方法的设计和编码上,但是但我们真正弄懂了事件的真正出发执行原理的话,对我们的编程的提高真是非常榜的,例如在windows编程中 如果我单击了一个button按钮触发了button 的click事件  Button1_Click(){} ,但是有时候我们编程的时候,不但想要触发button 的单击事件,我还想要把其他的时间也要调用下来顺序执行,要实现这种方式,除了在方法最后对其他方法的调用,还可以利用将其

  • c#委托与事件(详解)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在.本文中,我将通过两个范例由浅入深地讲述什么是委托.为什么要使用委托.事件的由来..Net Framework中的委托和事件.委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论. 将方法作为方法的参数 我们先不管这个标题

  • C#中委托和事件的区别实例解析

    本文实例分析了C#中委托和事件的区别,分享给大家供大家参考之用.具体如下: 大致来说,委托是一个类,该类内部维护着一个字段,指向一个方法.事件可以被看作一个委托类型的变量,通过事件注册.取消多个委托或方法.本篇分别通过委托和事件执行多个方法,从中体会两者的区别. 一.通过委托执行方法 class Program { static void Main(string[] args) { Example example = new Example(); example.Go(); Console.Re

  • C#零基础学习理解委托

    说来惭愧,在大学的课程中,竟然没有听说过委托这个名称.那么今天我就带着大家一起探讨下委托和事件. 咱们先来看下委托 我主要从以下几个方面讲解 1,  为什么使用委托  2.什么是委托  3.委托如何使用 为什么使用委托? 委托是c#中非常重要的一个概念,使用委托使程序员可以将方法引用封装在委托对象内.然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法.与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的. 什么是委托? 委托是一种引用方法的类型,一旦为委

  • C# 委托的三种调用示例(同步调用 异步调用 异步回调)

    首先,通过代码定义一个委托和下面三个示例将要调用的方法: 复制代码 代码如下: public delegate int AddHandler(int a,int b);    public class 加法类    {        public static int Add(int a, int b)        {            Console.WriteLine("开始计算:" + a + "+" + b);            Thread.Sl

  • C#中委托的+=和-=深入研究

    写在前面 为什么会突然想说说委托?原因吗,起于一个同事的想法,昨天下班的路上一直在想这个问题,如果给委托注册多个方法,会不会都执行呢?为了一探究性,就弄了个demo研究下. += 大家都知道委托都继承自System.MulticastDelegate,而System.MulticastDelegate又继承自System.Delegate,可以通过+=为委托注册多个方法.那么他们是否都执行了呢?执行的结果又是怎样的呢?有返回值和没返回值的是否结果是否一样?那就试着说说+=都干了哪些事? 测试代码

  • c#委托把方法当成参数(实例讲解)

    静态方法代理: 复制代码 代码如下: public delegate void DoGreeting(string name); class Program    {        [STAThread]        static void Main(string[] args)        {            //方法名当成参数传给委托类型调用            MarkGreeting("张三", GreetingEnglish);            MarkGr

  • c#委托详解和和示例分享

    什么是委托? 委托是寻址方法的.NET版本,使用委托可以将方法作为参数进行传递.委托是一种特殊类型的对象,其特殊之处在于委托中包含的只是一个活多个方法的地址,而不是数据. 委托虽然看起来像是一种类型,但其实定义一个委托,是定义了一个新的类.下面这行代码,定义了一个委托,使用ILDasm.exe查看其生成的IL代码如图所示: 复制代码 代码如下: //定义委托,它定义了可以代表的方法的类型,但其本身却是一个类 public delegate int methodDelegate(string st

随机推荐