深圳哪些公司做网站,制作网站的公司不干了,顺德大良那里做网站好,一个简单的登录界面网页代码目录
C# 委托#xff08;Delegate#xff09;
委托概述
使用委托
带有命名方法的委托与匿名方法
示例
如何合并委托#xff08;多播委托#xff09;
示例
如何声明、实例化和使用委托
示例 C# 委托#xff08;Delegate#xff09;
委托是一种引用类型#xff…目录
C# 委托Delegate
委托概述
使用委托
带有命名方法的委托与匿名方法
示例
如何合并委托多播委托
示例
如何声明、实例化和使用委托
示例 C# 委托Delegate
委托是一种引用类型表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。
委托用于将方法作为参数传递给其他方法。 事件处理程序就是通过委托调用的方法。 你可以创建一个自定义方法当发生特定事件时某个类如 Windows 控件就可以调用你的方法。 下面的示例演示了一个委托声明
public delegate int PerformCalculation(int x, int y);可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。 该方法可以是静态方法也可以是实例方法。 此灵活性意味着你可以通过编程方式来更改方法调用还可以向现有类中插入新代码。
注意在方法重载的上下文中方法的签名不包括返回值。 但在委托的上下文中签名包括返回值。 换句话说方法和委托必须具有相同的返回类型。
将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。 可编写一个比较应用程序中两个对象的方法。 该方法可用在排序算法的委托中。 由于比较代码与库分离因此排序方法可能更常见。
对于类似的方案已将函数指针添加到 C# 9其中你需要对调用约定有更多的控制。 使用添加到委托类型的虚方法调用与委托关联的代码。 使用函数指针可以指定不同的约定。
委托概述
委托具有以下属性
类似于C的函数指针但是完全面向对象。与C的函数指针不同委托会同时封装对象实例和方法。允许将方法作为参数进行传递这使得委托非常灵活可以用于定义回调方法。可以将多个委托链接在一起例如可以对一个事件调用多个方法从而实现多播委托的功能。方法不必与委托类型完全匹配这意味着可以使用变体来适应不同的方法签名。有关详细信息请参阅使用委托中的变体。使用Lambda表达式可以更简洁地编写内联代码块。在某些上下文中Lambda表达式可以编译为委托类型从而进一步简化了代码编写。 若要详细了解 lambda 表达式请参阅 lambda 表达式。
总的来说委托作为C#中的重要特性为开发人员提供了一种非常灵活和强大的方法用于处理回调、事件处理以及动态方法调用等场景。
使用委托
委托是安全封装方法的类型类似于 C 和 C 中的函数指针。 与 C 函数指针不同的是委托是面向对象的、类型安全的和可靠的。 委托的类型由委托的名称确定。 以下示例声明名为 Callback 的委托该委托可以封装采用字符串作为参数并返回 void 的方法
public delegate void Callback(string message);
委托对象通常可采用两种方式进行构造一种是提供委托将封装的方法的名称另一种是使用 lambda 表达式。 以这种方式实例化委托后可以调用该委托。 调用委托会调用附加到委托实例的方法。 调用方传递到委托的参数将传递到该方法并且委托会将方法的返回值如果有返回到调用方。 例如
// 为委托创建方法
public static void DelegateMethod(string message)
{Console.WriteLine(message);
}
// 实例化委托。
Callback handler DelegateMethod;// 使用委托。
handler(Hello World);
委托类型派生自 .NET 中的 Delegate 类。 委托类型是密封的它们不能派生自 Delegate也不能从其派生出自定义类。 由于实例化的委托是一个对象因此可以作为实参传递或分配给一个属性。 这允许方法接受委托作为参数并在稍后调用委托。 这被称为异步回调是在长进程完成时通知调用方的常用方法。 当以这种方式使用委托时使用委托的代码不需要知道要使用的实现方法。 功能类似于封装接口提供的功能。
回调的另一个常见用途是定义自定义比较方法并将该委托传递到短方法。 它允许调用方的代码成为排序算法的一部分。 以下示例方法使用 Del 类型作为参数
public static void MethodWithCallback(int param1, int param2, Callback callback)
{callback(The number is: (param1 param2).ToString());
}
然后你可以将上面创建的委托传递到该方法
MethodWithCallback(1, 2, handler);
并将以下输出接收到控制台
The number is: 3
以抽象方式使用委托时MethodWithCallback 不需要直接调用控制台记住其不必设计为具有控制台。 MethodWithCallback 的作用是简单准备字符串并将字符串传递到其他方法。 由于委托的方法可以使用任意数量的参数此功能特别强大。
当委托构造为封装实例方法时委托将同时引用实例和方法。 委托不知道除其所封装方法以外的实例类型因此委托可以引用任何类型的对象只要该对象上有与委托签名匹配的方法。 当委托构造为封装静态方法时委托仅引用方法。 请考虑以下声明
public class MethodClass
{public void Method1(string message) { }public void Method2(string message) { }
}
加上之前显示的静态 DelegateMethod我们现在已有三个 Del 实例可以封装的方法。
调用时委托可以调用多个方法。 这被称为多播。 若要向委托的方法列表调用列表添加其他方法只需使用加法运算符或加法赋值运算符“”或“”添加两个委托。 例如
var obj new MethodClass();
Callback d1 obj.Method1;
Callback d2 obj.Method2;
Callback d3 DelegateMethod;//这两种类型的分配都是有效的
Callback allMethodsDelegate d1 d2;
allMethodsDelegate d3;
此时allMethodsDelegate 的调用列表中包含三个方法分别为 Method1、Method2 和 DelegateMethod。 原有的三个委托d1、d2 和 d3保持不变。 调用 allMethodsDelegate 时将按顺序调用所有三个方法。 如果委托使用引用参数引用将按相反的顺序传递到所有这三个方法并且一种方法进行的任何更改都将在另一种方法上见到。 当方法引发未在方法内捕获到的异常时该异常将传递到委托的调用方并且不会调用调用列表中的后续方法。 如果委托具有返回值和/或输出参数它将返回上次调用方法的返回值和参数。 若要删除调用列表中的方法请使用减法运算符或减法赋值运算符- 或 -。 例如
//remove Method1
allMethodsDelegate - d1;// 删除 d2 时复制 AllMethodsDelegate
Callback oneMethodDelegate allMethodsDelegate - d2;
由于委托类型派生自 System.Delegate因此可以在委托上调用该类定义的方法和属性。 例如若要查询委托调用列表中方法的数量你可以编写
int invocationCount d1.GetInvocationList().GetLength(0);
调用列表中具有多个方法的委托派生自 MulticastDelegate该类属于 System.Delegate 的子类。 由于这两个类都支持 GetInvocationList因此在其他情况下上述代码也将产生作用。
多播委托广泛用于事件处理中。 事件源对象将事件通知发送到已注册接收该事件的接收方对象。 若要注册一个事件接收方需要创建用于处理该事件的方法然后为该方法创建委托并将委托传递到事件源。 事件发生时源调用委托。 然后委托将对接收方调用事件处理方法从而提供事件数据。 给定事件的委托类型由事件源确定。 有关详细信息请参阅事件。
在编译时比较分配的两个不同类型的委托将导致编译错误。 如果委托实例是静态的 System.Delegate 类型则允许比较但在运行时将返回 false。 例如
delegate void Callback1();
delegate void Callback2();static void method(Callback1 d, Callback2 e, System.Delegate f)
{// Compile-time error.//Console.WriteLine(d e);// OK at compile-time. False if the run-time type of f// is not the same as that of d.Console.WriteLine(d f);
}
带有命名方法的委托与匿名方法
委托可以与命名方法相关联。 使用命名方法实例化委托时该方法作为参数传递例如
// 声明一个委托
delegate void WorkCallback(int x);// 定义命名方法
void DoWork(int k) { /* ... */ }// 使用方法作为参数实例化委托
WorkCallback d obj.DoWork;
这称为使用命名方法。 使用命名方法构造的委托可以封装静态方法或实例方法。 命名方法是在早期版本的 C# 中实例化委托的唯一方式。 但是如果创建新方法会造成多余开销C# 允许你实例化委托并立即指定调用委托时委托将处理的代码块。 代码块可包含 Lambda 表达式或匿名方法。
作为委托参数传递的方法必须具有与委托声明相同的签名。 委托实例可以封装静态方法或实例方法。
注意尽管委托可以使用 out 参数但不建议将该委托与多播事件委托配合使用因为你无法知道将调用哪个委托。
从 C# 10 开始包含单个重载的方法组具有自然类型。 这意味着编译器可以推断委托类型的返回类型和参数类型
var read Console.Read; // 只有一个过载;Funcint推断
var write Console.Write; // 错误多个重载无法选择
示例
using System;public delegate void MyDelegate(string message);public class Program
{public static void Main(){// 使用命名方法创建委托实例MyDelegate delegate1 new MyDelegate(Method1);delegate1(来自命名方法的Hello);// 使用匿名方法创建委托实例MyDelegate delegate2 delegate (string message){Console.WriteLine(匿名方法: message);};delegate2(来自匿名方法的Hello);}// 命名方法public static void Method1(string message){Console.WriteLine(方法1: message);}
}在这个示例中我们首先定义了一个名为MyDelegate的委托类型它接受一个字符串参数并无返回值。然后在Main方法中我们创建了两个委托实例delegate1和delegate2。
其中delegate1使用命名方法Method1来初始化并调用delegate1来触发委托从而调用关联的命名方法。
delegate2使用匿名方法来初始化并调用delegate2来触发委托从而调用关联的匿名方法。
如何合并委托多播委托
使用运算符可以将多个委托对象分配到一个委托实例中从而创建一个多播委托。这个多播委托包含了一个已经分配好的委托列表调用多播委托时会依次调用列表中的委托。
只有类型相同的委托实例才能通过使用运算符进行合并否则会产生编译错误。
另外- 运算符也可用于从多播委托中删除组件委托。例如如果我们有一个多播委托delegateChain其中包含了两个委托对象delegate1和delegate2我们可以使用 - 运算符将其中一个委托对象从多播委托中删除
delegateChain delegateChain - delegate1; // 从多播委托中删除 delegate1这将从delegateChain中删除delegate1使得delegateChain中仅包含delegate2委托对象。
示例
using System;public delegate void MyDelegate(string message);class Program
{static void Main(){MyDelegate delegate1 new MyDelegate(Method1);MyDelegate delegate2 new MyDelegate(Method2);MyDelegate delegate3 new MyDelegate(Method3);// 合并多个委托对象到一个多播委托MyDelegate delegateChain delegate1 delegate2 delegate3;// 调用多播委托将依次调用列表中的委托delegateChain(第一次依次调用列表中的委托);Console.WriteLine();// 从多播委托中删除一个委托对象delegateChain delegateChain - delegate2;// 再次调用多播委托将不再调用已删除的委托对象delegateChain(第二次依次调用列表中的委托);}static void Method1(string message){Console.WriteLine(Method1: message);}static void Method2(string message){Console.WriteLine(Method2: message);}static void Method3(string message){Console.WriteLine(Method3: message);}
}运行结果如下所示
Method1: 第一次依次调用列表中的委托
Method2: 第一次依次调用列表中的委托
Method3: 第一次依次调用列表中的委托Method1: 第二次依次调用列表中的委托
Method3: 第二次依次调用列表中的委托
在这个示例中我们定义了三个不同的委托对象delegate1、delegate2和delegate3它们都指向不同的方法。我们使用运算符将它们合并为一个多播委托delegateChain并调用它来依次调用列表中的委托。
然后我们使用-运算符从delegateChain中删除了一个委托对象delegate2。再次调用delegateChain时已删除的委托对象delegate2不再被调用。
如何声明、实例化和使用委托
可以使用以下任一方法声明委托
使用匹配签名声明委托类型并声明方法。将方法组分配给委托类型。声明匿名方法。使用 lambda 表达式。有关详细信息请参阅 Lambda 表达式。
以下是使用不同方法声明委托的示例代码
using System;// 1. 使用匹配签名声明委托类型并声明方法
public delegate void MyDelegate(string message);class Program
{static void Main(){// 2. 将方法分配给委托类型MyDelegate delegate1 new MyDelegate(Method1);delegate1(将方法分配给委托类型);// 3. 声明匿名方法并赋给委托MyDelegate delegate2 delegate (string message) { Console.WriteLine(匿名方法: message); };delegate2(声明匿名方法并赋给委托);// 4. 使用 lambda 表达式MyDelegate delegate3 (message) { Console.WriteLine(Lambda表达式: message); };delegate3(使用 lambda 表达式);}static void Method1(string message){Console.WriteLine(Method1: message);}
}在这个示例中我们演示了四种不同的方法来声明委托和分配方法
我们首先使用匹配签名声明了一个MyDelegate类型的委托并声明了一个方法Method1它的签名和委托类型匹配。然后我们将命名方法Method1分配给了delegate1委托对象并调用它。接着我们声明了一个匿名方法并将其分配给了delegate2委托对象并调用它。最后我们使用lambda表达式声明了一个方法并将其分配给了delegate3委托对象并调用它。
示例
以下是一个简单的示例演示如何在C#中声明、实例化和使用委托
using System;// 声明一个委托类型它接受两个整数参数并返回一个整数
public delegate int MyDelegate(int x, int y);class Program
{static void Main(){// 实例化委托对象并将其分配给命名方法MyDelegate delegate1 new MyDelegate(Method1);// 使用委托对象调用已分配的方法int result1 delegate1(10, 5);Console.WriteLine(结果1: result1);// 重新分配委托对象到匿名方法MyDelegate delegate2 delegate (int x, int y) { return x - y; };// 使用委托对象调用已分配的匿名方法int result2 delegate2(10, 5);Console.WriteLine(结果2: result2);// 重新分配委托对象到 lambda 表达式MyDelegate delegate3 (x, y) x * y;// 使用委托对象调用已分配的 lambda 表达式int result3 delegate3(10, 5);Console.WriteLine(结果3: result3);}// 命名方法它与 MyDelegate 委托类型具有相同的签名static int Method1(int x, int y){return x y;}
}在这个示例中我们首先声明了一个委托类型MyDelegate它接受两个整数参数并返回一个整数。然后我们实例化了三个委托对象并将它们分配给不同的方法、匿名方法和lambda表达式。我们使用这些委托对象调用已分配的方法、匿名方法和lambda表达式并输出结果。