草庐IT

C#-委托的详细用法

真的没事鸭 2023-12-24 原文

什么是委托

委托是一种函数引用的Object,实际上是用类完成了函数指针的功能

C#中的委托(Delegate)类似于C或C++中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生 自System.Delegate 类。

委托的使用

1,声明委托:

Delegate void Print(int x)

2,初始化,给委托赋值

  • 方法1:Print someFunc=hello;
  • 方法2:Print someFunc=new Print(hello);

3,调用委托变量

SomeFunc(x);

委托的分类

  • 委托静态方法:委托一个静态方法
  • 委托实例方法:委托一个实例方法

委托的原理

委托是将函数指针和实例对象打包在一起的类,它有两个重要的成员,一个用来保存实例对象,一个用来保存函数的指针。从源码中可以查看System.Delegate

 我们查看System.Delegate的属性,可以看到一个属性Target

public Object Target
{
    get
    {
        return GetTarget();
    }
}

看一下GetTarget的功能

internal virtual Object GetTarget()
{
    return (_methodPtrAux.IsNull())?_target:null;
}

意思就是当把一个静态方法给委托的时候将会返回一个null,如果是一个实例方法时, 将会返回当前方法所在的实例对象(this)

下面用一段代码演示一下:

using System;
//声明委托
public delegate void Print(int x);
namespace ConsoleApp1
{
    public class Test
    {
        //实例方法
        public void Test1(int x)
        {
            Console.WriteLine("实例方法,数字:{0}",x);
        }
        //静态方法
        public static void Test2(int x)
        {
            Console.WriteLine("静态方法,数字:{0}",x);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //委托的使用
            Test t = new Test();//实例对象
            Print p1 = new Print(t.Test1);//委托赋值
            p1(25);//调用委托
            Print p2 = Test.Test2;//委托赋值
            p2(33);//调用委托
            //委托原理
            Console.WriteLine(p1.Target is Test);//实例方法-判断Target是不是指向方法所在的对象
            Console.WriteLine(p2.Target is Test);//静态方法-判断Target是不是null
            Console.ReadLine();
        }
    }
}

输出结果

实例方法,数字:25
静态方法,数字:33
True
False

也就是如果Target属性为null说明是静态方法的委托,如果不为null说明是实例方法的 委托

需要注意的是声明的委托类型和被委托的函数类型以及参数要一致,如果委托没有返回 值,那么被委托的函数也要没有返回值,如果委托有参数,那么被委托的函数和委托的 参数类型要一致。

通过这个代码就可以很直白的看出委托的作用,就是将方法封装在委托中,然后就可以将委托对象传递给可调用所引用方法的代码,不必在编译时知道将要调用那个方法。

系统内置委托

前面是自己定义的委托,但是系统也提供了两种委托:Action和Func,Action用于不需 要返回值的委托,Func适合需要返回值的委托

Action委托

1,Action委托 封装一个方法,该方法不具有参数并且不返回值

2,Action<T>委托 封装一个方法,该方法只有一个参数并且不返回值

3,Action<T1,T2>委托 封装一个方法,该方法具有两个参数并且不返回值

最多有16个参数

Func委托

1,Func(TResult)委托封装封装一个不具有参数但却返回 TResult 参数指定的类型值的方 法

2,Func(T,TResult)委托 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法

3,Func(T1,T2,TResult)委托 封装一个具有两个参数并返回 TResult 参数指定的类型值的方 法

其中的T是参数类型,TResult是返回值类型

下面用一个例子演示:

using System;

namespace ConsoleApp1
{
    public class Test
    {
        //Action示例
        public void Test1(int x)
        {
            Console.WriteLine("打印数字:"+x);
        }
        //Func示例
        public int Test2(int n)
        {
            return n;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //系统内置委托的使用
            Test t = new Test();//实例对象
            Action<int> ac1 = new Action<int>(t.Test1);//委托赋值
            ac1(22);//调用委托
            Func<int, int> ac2 = new Func<int, int>(t.Test2);//委托赋值
            Console.WriteLine(ac2(233));//调用委托
            Console.ReadLine();
        }
    }
}

输出结果:

打印数字:22
233

多播委托

多播委托是指在一个委托中注册多个方法,在注册方法时可以在委托中使用加号运算符

或者减号运算符来实现添加或撤销方法。

比如现实的点餐系统,可以点甜食,面食,水果等,在这里委托相当于点餐平台,每一 个类型的商品可以理解为在委托商注册的一个方法。

使用委托的这个有用的特点,可以创建一个委托被调用时要调用的方法的调用列表。 这被称为委托的 多播(multicasting),也叫组播。

下面的程序演示了委托的多播:

using System;

//声明委托
public delegate void Order();
namespace ConsoleApp1
{
    public class Test
    {
        //买水果
        public void Test1()
        {
            Console.WriteLine("购买水果!");
        }
       //买甜食
       public static void Test2()
        {
            Console.WriteLine("购买甜食!");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //多播委托
            Test t = new Test();//实例对象
            Order order =new Order(t.Test1);//委托赋值-实例方法
            order += new Order(Test.Test2);//委托赋值-静态方法
            order();//调用委托
            Console.ReadLine();
        }
    }
}

输出结果:

购买水果!
购买甜食!

通过这个例子可以看出来,我们把两个方法都注册到了一个委托上面,并且调用委托一 下调用了两个方法,先调用了购买水果的方法,然后调用了购买甜食的方法。

在使用多播委托时需要注意,在委托中注册的方法参数列表必须与委托定义的参数列表 相同,否则不能将方法添加到委托上。

如有错漏之处,敬请指正!

有关C#-委托的详细用法的更多相关文章

  1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  2. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  5. ruby - 有人可以解释一下在 Ruby 中注入(inject)的真实、通俗易懂的用法吗? - 2

    我正在学习Ruby,遇到了inject。我正处于理解它的风口浪尖,但当我是那种需要真实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用inject来添加一个(1..10)范围的总和,我不太关心这个。这是一个任意的例子。在实际程序中我会用它做什么?我正在学习,所以我可以继续使用Rails,但我不必有一个以Web为中心的示例。我只需要一些我可以全神贯注的目标。谢谢大家。 最佳答案 inject有时可以通过它的“其他”名称reduce更好地理解。它是一个对Enumerable进行操作(迭代一次)并返回单个值的函数。它有许多有

  6. ruby - 将属性方法委托(delegate)给父对象 - 2

    我有以下类(class):classAlphabetattr_reader:letter_freqs,:statistic_letterdefinitialize(lang)@lang=langcaselangwhen:en@alphabet=('A'..'Z').to_a@letter_freqs={...}when:ru@alphabet=('А'..'Я').to_a.insert(6,'Ё')@letter_freqs={...}...end@statistic_letter=@letter_freqs.max_by{|k,v|v}[0]endendfoo=Alphabet.n

  7. c# - C# 中的 Flatten Ruby 方法 - 2

    我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

  8. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

    我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

  9. c# - Ruby 等效于 C# Linq 聚合方法 - 2

    什么是Linq聚合方法的ruby​​等价物。它的工作原理是这样的varfactorial=new[]{1,2,3,4,5}.Aggregate((acc,i)=>acc*i);每次将数组序列中的值传递给lambda时,变量acc都会累积。 最佳答案 这在数学以及几乎所有编程语言中通常称为折叠。它是更普遍的变形概念的一个实例。Ruby从Smalltalk中继承了这个特性的名称,它被称为inject:into:(像aCollectioninject:aStartValueinto:aBlock一样使用。)所以,在Ruby中,它称为inj

  10. ruby - 使用法拉第上传文件 - 2

    我在尝试使用Faraday将文件上传到网络服务时遇到问题。我的代码:conn=Faraday.new('http://myapi')do|f|f.request:multipartendpayload={:file=>Faraday::UploadIO.new('...','image/jpeg')}conn.post('/',payload)尝试发布后似乎没有任何反应。当我检查响应时this是我所看到的:#:post,:body=>#,#,@opts={}>,#],@index=0>>,#>],@ios=[#,#,@opts={}>,#],@index=0>,#],@index=0>

随机推荐