如果我们想传递一个函数作为参数怎么办? C# 如何处理回调函数或事件处理程序? 答案是——委托。
委托是定义方法签名的引用类型数据类型。 您可以定义委托的变量,就像其他数据类型一样,可以引用与委托具有相同签名的任何方法。
我们使用委托时会涉及到三个步骤:
可以使用 delegate 关键字后跟函数签名来声明委托,如下所示。
[访问修饰符] delegate [返回类型] [委托名称]([参数...])
下面声明了一个名为 MyDelegate 的委托。
public delegate void MyDelegate(string msg);
上面,我们已经声明了一个具有 void 返回类型和字符串参数的委托 MyDelegate。 委托可以在类外或类内声明。 推荐 声明在类之外。
声明委托后,我们需要设置目标方法或 lambda 表达式。 我们可以通过使用 new 关键字创建委托对象并传递签名与委托签名匹配的方法来实现。
public delegate void MyDelegate(string msg); // 声明一个委托
class Program
{
// 跟MyDelegate有相同参数和返回值的方法 称为签名相同
static void MethodA(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
// 把函数传给委托
MyDelegate del1 = new MyDelegate(MethodA);
// 直接赋值给委托
MyDelegate del2 = MethodA;
// 使用 lambda表达式
MyDelegate del3 = (string msg) => Console.WriteLine(msg);
Console.ReadKey();
}
}
设置目标方法后,可以使用 Invoke() 方法或使用 () 运算符调用委托。
del1.Invoke("1");
del2("2");
完整的代码如下
using System;
namespace ConsoleApp1
{
public delegate void MyDelegate(string msg); // 声明一个委托
class Program
{
// 跟MyDelegate有相同参数和返回值的方法
static void MethodA(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
// 把函数传给委托
MyDelegate del1 = new MyDelegate(MethodA);
// 直接赋值给委托
MyDelegate del2 = MethodA;
// 使用 lambda表达式
MyDelegate del3 = (string msg) => Console.WriteLine(msg);
del1.Invoke("1");
del2("2");
del3("3");
Console.ReadKey();
}
}
}
输出
1
2
3
一个方法可以有一个委托类型的参数,如下所示。
using System;
namespace ConsoleApp1
{
public delegate void MyDelegate(string msg); // 声明一个委托
class Program
{
static void Main(string[] args)
{
MyDelegate del = ClassA.MethodA;
InvokeDelegate(del);
del = ClassB.MethodB;
InvokeDelegate(del);
del = (string msg) => Console.WriteLine("调用 lambda 表达式: " + msg);
InvokeDelegate(del);
}
static void InvokeDelegate(MyDelegate del) // 参数是一个委托
{
del("Hello World");
}
}
public class ClassA
{
public static void MethodA(string message)
{
Console.WriteLine("调用 ClassA.MethodA(): " + message);
}
}
public class ClassB
{
public static void MethodB(string message)
{
Console.WriteLine("调用 ClassB.MethodB(): " + message);
}
}
}
输出
调用 ClassA.MethodA(): Hello World
调用 ClassB.MethodB(): Hello World
调用 lambda 表达式: Hello World
注意有了泛型之后,我们通常是用Action<>和Func<>而不在自已定义委托了。 Action<> 没有返回值 Func是有返回值的。 如下示例。
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Action<string> del = ClassA.MethodA;
InvokeDelegate(del);
del = (string msg) => Console.WriteLine("调用 lambda 表达式: " + msg);
InvokeDelegate(del);
}
static void InvokeDelegate(Action<string> del)
{
del("Hello World");
}
}
public class ClassA
{
public static void MethodA(string message)
{
Console.WriteLine("调用 ClassA.MethodA(): " + message);
}
}
}
上面的Action实际是定义在了System下面的 代码如下
public delegate void Action<in T>(T obj);
委托可以指向多个方法。 指向多个方法的委托称为多播委托。 “+”或“+=”运算符将函数添加到调用列表中,“-”和“-=”运算符将其删除。
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Action<string> del = ClassA.MethodA;
del += ClassB.MethodB; //使用了 += del=del+ClassB.MethodB
InvokeDelegate(del);
del -= ClassB.MethodB;
del("hello world 2");
Console.ReadLine();
}
static void InvokeDelegate(Action<string> del) // 参数是一个委托
{
del("Hello World");
}
}
public class ClassA
{
public static void MethodA(string message)
{
Console.WriteLine("调用 ClassA.MethodA(): " + message);
}
}
public class ClassB
{
public static void MethodB(string message)
{
Console.WriteLine("调用 ClassB.MethodB(): " + message);
}
}
}
加法和减法运算符始终作为赋值的一部分工作: del += ClassB.MethodB;
完全等同于 del = del+ ClassB.MethodB;
减法也一样。
如果delegate 有返回值的话,哪么只会把最后调用的方法的值返回出来。
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Func<int> del = ClassA.MethodA;
del += ClassB.MethodB;
var result = del();
Console.WriteLine(result); //输出 200
}
}
public class ClassA
{
public static int MethodA()
{
Console.WriteLine("调用 ClassA.MethodA(): ");
return 100;
}
}
public class ClassB
{
public static int MethodB()
{
Console.WriteLine("调用 ClassB.MethodB(): ");
return 200;
}
}
}
全部输出
调用 ClassA.MethodA():
调用 ClassB.MethodB():
200
泛型委托的定义方式与委托相同,但使用泛型类型参数或返回类型。 设置目标方法时必须指定泛型类型。
例如,考虑以下用于 int 和 string 参数的通用委托。
using System;
namespace ConsoleApp1
{
public delegate T add<T>(T param1, T param2); // 泛型委托
class Program
{
static void Main(string[] args)
{
add<int> sum = Sum;
Console.WriteLine(sum(10, 20)); //输出 30
add<string> con = Concat;
Console.WriteLine(con("Hello ", "World!!"));//输出 Hello World!!
}
public static int Sum(int val1, int val2)
{
return val1 + val2;
}
public static string Concat(string str1, string str2)
{
return str1 + str2;
}
}
}
正常来说我们会用系统定义好的Action<>和Func<>。而不需要自己定义, 下面的章节会介绍到。