您当前的位置:首页 > 文章 > C# 委托(delegate) 详解

C# 委托(delegate) 详解

作者:soar+ 时间:2023-12-05 阅读数:316 人阅读
概念:
类似C/C++的函数指针。委托是一种引用类型变量, 存有一系列对象方法的地址,是对一系列方法的引用的一种引用类型变量。调用委托的时候,它所引用的所有方法都会被执行

通俗来说。委托对象是用来存放某一类别的方法调用清单,这一类别要求方法签名(方法的参数,返回值)与委托类型签名一致。当这个清单被调用时,清单内的方法也会依次执行

.NET 自带的委托
.NET提供了两个自带的委托类型。两种委托类型已经能够胜任绝大部分开发需求。它们分别是 Action<T>与Func<T,TResult> 委托。
=>T:此委托封装的方法的参数类型。
=>TResult:此委托封装的方法的返回值类型。
T与TResult都为逆变类型参数。即,可以使用指定的类型,也可以使用派生程度更低的类型。

两种委托类型的区别是什么?
对于一个没有返回值的方法,应该使用Action封装。如果一个方法带有返回值 则使用Func。

Action委托
1.简单举例:

    internal class Program
    {
        static void Main(string[] args)
        {
            Boy boy = new Boy("Jack");
            Action a1 = new Action(boy.Eat);
            a1.Invoke();

            Action<int> a2 = new Action<int>(boy.Sleep);
            a2(10);
        }

        class Boy
        {
            public Boy(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
            public void Eat()
            {
                Console.WriteLine($"男孩 {Name} 正在吃东西...");
            }

            public void Sleep(int time)
            {
                Console.WriteLine($"男孩 {Name} 一共睡了 {time} 小时");
            }
        }
        
    }
此例声明一个Boy类 中含有两个方法。Eat方法无参数,Sleep需要一个Int参数.且都无返回值。
如果需要使用Action包装boy的两个方法我们就需要声明与此方法相同的签名的Action对象。如上例所示 分别声明了两个签名与方法相同的Action对象。

2.对于委托对象时有几种不同的声明方式和调用方式,下面出了举例:

            Action a1 = new Action(boy.Eat);//声明方式1
            Action a2 = boy.Eat;//声明方式2
            Action a3 = () => {//声明方式3
                boy.Eat();
                Console.WriteLine("Lambda表达式声明...");
            a1.Invoke();//调用方式1
            a2();//调用方式2

            };


声明方式: 1.new关键字 2.直接赋值 3.Lambda表达式

调用方式: 1.对象名.Invoke(参数) 2.对象名(参数)


3.下面是对Action对象的一些定义案例
Action委托至少0个参数,至多16个参数,无返回值。
Action 表示无参,无返回值的委托。
Action<int,string> 表示有传入参数int,string无返回值的委托。
Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托。
Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托。

Func委托
1.简单举例

        static void Main(string[] args)
        {
            Boy boy = new Boy("Jack");
            Func<int,int,int> f1 = new Func<int, int, int>(boy.DoSum);
            Console.WriteLine(f1.Invoke(1,1));

            Func<double, double, double> f2 = new Func<double, double, double>(boy.DoDiv);
            Console.WriteLine(f2.Invoke(10,3));
        }

        class Boy
        {
            public Boy(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
            
            public int DoSum(int x,int y)
            {
                return x + y;
            }

            public double DoDiv(double x, double y)
            {
                return x / y;
            }
        }

上例中定义了两个方法,方法分别有着不同的返回值类型。在声明func委托时应该注意签名的一致。
DoSum返回值为int,并且拥有两个int类型参数。所以使用委托类型Func<int,int,int>(此处的第一个int为TResult即返回值类型)
DoDiv返回值为double,并且拥有两个int类型参数。所以使用委托类型Func<double,int,int>(此处的第一个int为TResult即返回值类型)

自定义委托(delegate)
首先要撇清的关系
delegate和Delegate的区别
1.Delegate 是一个不折不扣的类。
2.delegate 是关键字,用来声明委托类。
3.类 Delegate 是委托类型的基类。 但是,只有系统和编译器才能从 Delegate 类或 MulticastDelegate 类显式派生。 此外,不允许从委托类型派生新类型。

声明
因为委托delegate是一种类。所以声明时请与class同级,一般情况下不需要嵌套class声明委托类。
1.委托类声明格式:
修饰符 delegate 返回值类型 委托名 ( 参数列表 );
举个栗子:
public delegate void Mydelegate();

2.声明此委托对象的格式:
委托名 委托对象名 = new 委托名 ( 方法名 );
举个栗子:
Mydelegate d1 = new Mydelegate( 方法名 );
这里需要注意方法与委托对象签名的一致性

简单示例
    public delegate int Mydelegate(int a, int b);
    internal class Program
    {
        static void Main(string[] args)
        {
            Boy boy = new Boy("jack");
            Mydelegate dosum = new Mydelegate(boy.DoSum);
            Console.WriteLine(dosum(1,5));
        }

        class Boy
        {
            public Boy(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
            
            public int DoSum(int x,int y)
            {
                return x + y;
            }

        }
        
    }

为了与public int DoSum(int x,int y)有相同的签名。此处声明了返回值为int 同时带两个参数为int的委托类 Mydelegate

合并委托(多播)
概念
在概念中说过。委托对象是用来存放一系列方法的调用清单,这说明一个委托对象中可以同时存放多个方法。但前提是方法的签名必须与委托一致。
使用+=和-=操作符来添加/删除委托中的方法
格式:
委托对象 += 方法名
委托对象 -= 方法名

简单举例
    public delegate void Mydelegate();
    internal class Program
    {
        static void Main(string[] args)
        {
            Boy boy = new Boy("jack");
            Mydelegate d1 =new Mydelegate(boy.DoHomeWork);
            d1+= boy.Jump;
            d1+= boy.Think;
            d1.Invoke();
        }

        class Boy
        {
            public Boy(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
            
            public void DoHomeWork()
            {
                Console.WriteLine($"{Name} 正在做作业...");
            }

            public void Jump()
            {
                Console.WriteLine($"{Name} 正在跳...");
            }

            public void Think()
            {
                Console.WriteLine($"{Name} 正在思考...");
            }
        }
        
    }

输出:
jack 正在做作业...
jack 正在跳...

jack 正在思考...

异步委托与同步委托
前排提示
??前排提示:异步委托在.net core中已经被砍掉
详细相关:
https://devblogs.microsoft.com/dotnet/migrating-delegate-begininvoke-calls-for-net-core/

概念
上文中提过调用委托的方式有这样一种 => 委托对象.Invoke()
委托对象.Invoke() 是以同步委托的方式来调用此方法
此外还有另一种 =>委托对象.Begininvoke()

1.同步委托调用 Invoke()
2.异步委托调用 Begininvoke(callback,object)

同步委托上文已经提过。下文主要提提异步委托

BeginInvoke 方法启动异步调用。 该方法具有与你要异步执行的方法相同的参数,另加两个可选参数。 第一个参数是一个 AsyncCallback 委托,此委托引用在异步调用完成时要调用的方法。 第二个参数是一个用户定义的对象,该对象将信息传递到回调方法。 BeginInvoke 将立即返回,而不会等待异步调用完成。 BeginInvoke 返回可用于监视异步调用的进度的 IAsyncResult。

简单示例
internal class Program
{
static void Main(string[] args)
{
Boy boy1 = new Boy(“Jack”, ConsoleColor.DarkBlue);
Boy boy2 = new Boy(“Bob”, ConsoleColor.Red);
Boy boy3 = new Boy(“Anna”, ConsoleColor.Yellow);
Action a1 = boy1.DoHomeWork;
Action a2 = boy2.DoHomeWork;
Action a3 = boy3.DoHomeWork;
a1.BeginInvoke(boy1.FinshWork,boy1.Name);
a2.BeginInvoke(boy1.FinshWork, boy2.Name);
a3.BeginInvoke(boy1.FinshWork, boy3.Name);
}

class Boy
{
    public Boy(string name, ConsoleColor penColor)
    {
        Name = name;
        PenColor = penColor;
    }

    public string Name { get; set; }
    public ConsoleColor PenColor { get; set; }

    public void DoHomeWork()
    {
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine(Name+"正在做第 "+(i+1)+" 个作业...");
            Thread.Sleep(1000);
        }
    }

    public void FinshWork(IAsyncResult ar)
    {
        Console.WriteLine(ar.AsyncState +"做完了作业");
    }

}
————————————————
版权声明:本文为CSDN博主「soar+」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35652006/article/details/128928017

本站大部分文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了您的权益请来信告知我们删除。邮箱:1451803763@qq.com