C# 委托?这篇文章让你困惑全摆脱!
Posted CSDN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 委托?这篇文章让你困惑全摆脱!相关的知识,希望对你有一定的参考价值。
作者 | 羽生结弦
责编 | 胡雪蕊
出品 | CSDN(CSDNnews)
在C#中的委托关键字是 Delegate,委托类似于C/C++中函数的指针。是存有对某个方法引用的引用类型变量,可在运行时被改变。一般用于实现事件和回调方法。
注意:所有的委托都派生自 System.Delegate 类委托分为 委托类型和委托实例,下面分别进行讲解。
零、委托类型和委托实例
1. 委托类型
委托类型定义了委托实例可以调用的方法、方法的返回类型和参数。我们可以通过委托类型的返回类型和参数来得知具体可以调用哪种方法。下面我们通过一个例子来看一下:
(1)首先我们定义一个委托类型:
csharp
delegate string DemoDelegate(int num);
csharp
string IntToString(int num)
{
return num.ToString();
}
int StringToInt(string num)
{
return int.Parse(num);
}
2. 委托实例
csharp
static void Main(string[] args)
{
DemoDelegate dd = IntToString;
string num = dd(123);
// 将输出 string 类型 "123"
Console.WriteLine(num);
}
小知识:上面的代码我们还可以这样写,这两种写法是等价的:
csharp
static void Main(string[] args)
{
DemoDelegate dd = new DemoDelegate(IntToString);
string num = dd.Invoke(123);
// 将输出 string 类型 "123"
Console.WriteLine(num);
}
委托的用途很多,我们这里来看一个例子,这个例子展示了委托其中一种的用途
csharp
public delegate int DemoDelegate(int num);
class Tool
{
public static void IntSquare(int[] intArray, DemoDelegate dd)
{
for (int i = 0; i < intArray.Length; i++)
{
intArray[i] = dd(intArray[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
DemoDelegate dd = Square;
int[] intArray = {2,4,6 };
Tool.IntSquare(intArray, dd);
for (int i = 0; i < intArray.Length; i++)
{
Console.WriteLine(intArray[i]);
}
Console.Read();
}
static int Square(int num)
{
return num * num;
}
}
我们将委托提取出来,作为一个公共的,然后定义一个 Tool 类,其中定义了一个计算数组中每个值的方法,这个方法接受两个参数,一个是int类型的数组,另一个是 DemoDelegate 委托类型的参数。通过委托调用 Program 类中的 Square 方法来计算数组中每个数字的平方值。我们在 Main方法中将 Square 方法赋值给委托变量,然后见数组和委托变量一同传入刚才我们定义的 Tool 类中的 IntSquare 方法,最后输出值为:4、16、36。这种用途叫做编写插件式方法,插件式方法就是只有在运行时才将方法赋值给委托。
多播委托
csharp
Delegate d = method1;
d += method2;
csharp
d -= method1;
csharp
Delegate d = null;
d += method1;
csharp
d = method1;
csharp
public delegate int DemoDelegate(int num);
static void Main(string[] args)
{
DemoDelegate dd = null;
dd += Square;
dd += Remainder;
dd(5);
Console.Read();
}
static int Square(int num)
{
Console.WriteLine(num * num);
return num * num;
}
static int Remainder(int num)
{
Console.WriteLine(num % 2);
return num % 2;
}
注意:
1. 委托不可变,使用 **+=** 或者 **-=** 实际上是创建了新的委托实例,并把它付给当前的委托变量。
2. 如果多播委托的返回类型不是void,那么调用者只能获取到最后一个被调用方法的返回值,前面方法的返回值将会被抛弃。
3. c#会将 +、-、+=、-=编译为 Combine 和 Remove两个方法。
实例方法委托和静态方法委托
实例方法和静态方法都是c#中经常用到的方法,我们可以将这两种方法都赋值给委托,因此就出现了实例方法稳妥和静态方法委托。它们之间的区别如下:1. 一个实例方法被赋值给委托对象时,委托对象不仅要保留对方法的引用,还要保留方法所属实例的引用,这时 System.Delegate 中的Target 属性就表示的是方法所属的实例;
2. 一个静态方法赋值给委托对象时,Target 属性值为null。例子如下:- 首先定义一个类 Demo 里边包含 NumAdd 实例方法和 Num 静态方法
csharp
class Demo
{
public int NumAdd(int num)
{
return ++num;
}
public static int Num(int num)
{
return num;
}
}
接着在控制台中调用这两个方法
csharp
public delegate int DemoDelegate(int num);
class Program
{
static void Main(string[] args)
{
Demo demo = new Demo();
DemoDelegate dd = demo.NumAdd;
dd(2);
Console.WriteLine("方法所属实例:"+dd.Target);
Console.WriteLine("调用方法:"+dd.Method);
DemoDelegate staticDd = Demo.Num;
staticDd(2);
Console.WriteLine("方法所属实例:" + staticDd.Target);
Console.WriteLine("调用方法:" + staticDd.Method);
Console.ReadLine();
}
}
运行以上代码,输出结果如下:
我们可以看到,将静态方法赋值给委托对象后打印方法所属实例为空。
泛型委托类型
在一些情况下我们不确定参数类型和返回值类型,这时我们就需要用到泛型委托类型,语法如下:
csharp
public delegate T DemoDelegate<T>(T arg);
我们具体看一下例子:
csharp
public delegate T DemoDelegate<T>(T num);
class Demo
{
public int NumAdd(int num)
{
return ++num;
}
}
class Program
{
static void Main(string[] args)
{
Demo demo = new Demo();
DemoDelegate<int> dd = demo.NumAdd;
Console.WriteLine(dd(2));
Console.ReadLine();
}
}
csharp
public delegate string DemoDelegate<T>(T arg);
public delegate T DemoDelegate<T>(int arg);
Action 和 Func
1. Func
Func是一个具有返回类型的方法,它的类型参数数量可以多达16个,其中包括0到多个输入类型参数和一个输出类型参数。下面的代码段展示了Func部分类型参数:
csharp
delegate void Action();
delegate void Action<in T> (T t);
delegate void Action(in T1,in T2)(T t1,T t2);
2. Action
Action 是一个不具有返回类型的方法,他的类型参数数量同样多达16个。下面展示了部分Action类型参数:
csharp
delegate void Action();
delegate void Action<in T> (T t);
delegate void Action(in T1,in T2)(T t1,T t2);
我们来看一下例子,以Func为例,Action同理
csharp
class Demo
{
public void Num<T>(T[] array, Func<T, T> func)
{
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(func(array[i]));
}
}
}
class Program
{
static void Main(string[] args)
{
Demo demo = new Demo();
int[] array = new int[] {2,4,6 };
demo.Num<int>(array, NumAdd);
Console.ReadLine();
}
static int NumAdd(int num)
{
return ++num;
}
}
从代码中可以看出,我们将 Demo 类中的 Num 方法的第二个参数类型写成了 Func<T,T>,这里的意思是委托实例的返回类型和类型参数都是T类型。我们在Main函数中通过委托,控制台输出结果是3、5、7 。这时我们就看出了使用 Func 和 Action 的优点了,我们不需要在外部显式的定义委托,比较方便。
冷知识
1. 委托与接口
一般来说接口可以解决的问题,委托同样也可以解决,那么什么时候使用委托呢?我们来看一下:
(1)当需要多播的时候;
(2)订阅者需要多次实现接口的时候。
2. 委托兼容性
(1)委托类型
委托类型之间互不兼容,即使它们的签名一样也不行,也就是说如下的写法是错误的。
csharp
delegate void DD1();
delegate void DD2();
DD1 dd1=Method;
DD2 dd2=dd1;
如果委托实例具有相同的方法目标,那么委托实例就是相等的。
(3)参数
当调用一个方法时,提供的参数可以比方法参数更具体。例如被调用的方法参数是 Object类型,但是提供的参数是 String 类型,这时程序不会报错,因为string 来自 object,string 比 object 更具体。(委托只支持引用转换)
(4)返回值
同参数一样,当调用方法时,可以获得一个比被调用方法返回值更具体的返回值。
作者简介:朱钢,笔名羽生结弦,CSDN博客专家,.NET高级开发工程师,7年一线开发经验,参与过电子政务系统和AI客服系统的开发,以及互联网招聘网站的架构设计,目前就职于北京恒创融慧科技发展有限公司,从事企业级安全监控系统的开发。
以上是关于C# 委托?这篇文章让你困惑全摆脱!的主要内容,如果未能解决你的问题,请参考以下文章
对“xxx”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。 错误解决一例。(代码片段