C# 何时?;为什么要使用代理?
我在C#方面相对较新&我想知道何时适当地使用委托。 它们在事件声明中被广泛使用,但我应该在自己的代码中何时使用它们?它们为什么有用?为什么不使用其他东西? 我还想知道什么时候我必须使用代理,而我没有其他选择 谢谢你的帮助C# 何时?;为什么要使用代理?,c#,.net,delegates,C#,.net,Delegates,我在C#方面相对较新&我想知道何时适当地使用委托。 它们在事件声明中被广泛使用,但我应该在自己的代码中何时使用它们?它们为什么有用?为什么不使用其他东西? 我还想知道什么时候我必须使用代理,而我没有其他选择 谢谢你的帮助 编辑:我想我已经找到了一个强的>必要的用法:>/P>> P>我考虑委托人。在许多情况下,只要您需要一个带有单个方法的接口,就可以使用它们,但您不需要定义该接口的开销。当您想要声明要传递的代码块时,委托非常有用。例如,当使用通用重试机制时 伪: function Retry(De
<>编辑:我想我已经找到了一个<>强的>必要的用法:>/P>> P>我考虑委托人。在许多情况下,只要您需要一个带有单个方法的接口,就可以使用它们,但您不需要定义该接口的开销。当您想要声明要传递的代码块时,委托非常有用。例如,当使用通用重试机制时 伪:
function Retry(Delegate func, int numberOfTimes)
try
{
func.Invoke();
}
catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }
或者,当您想对代码块进行后期求值时,比如一个函数,其中您有一些Transform
操作,并且希望有一个BeforeTransform
和一个BeforeTransform
操作,您可以在您的Transform函数中求值,而不必知道BeginTransform
是否已填充,或者它必须改变什么
当然,在创建事件处理程序时也是如此。您不想现在就对代码求值,而是只在需要时才进行求值,因此您可以注册一个委托,该委托可以在事件发生时调用。我同意前面所说的一切,只是尝试在其上添加一些其他词语 委托可以被视为某个/某些方法的占位符 通过定义委托,您对类的用户说,“请随意将与此签名匹配的任何方法分配给委托,并且每次调用我的委托时都会调用它” 典型的用法当然是事件。所有OnEventX将委托给用户定义的方法 委托有助于为对象的用户提供一些自定义其行为的能力。 大多数情况下,您可以使用其他方法来实现相同的目的,我不相信您会被迫创建代理。在某些情况下,这只是完成任务的最简单方法 代表概述 委托具有以下属性:
- 委托允许将方法作为参数传递
- 委托可用于定义回调方法
- 可以将代表链接在一起;例如,可以对单个事件调用多个方法
- 方法不需要完全匹配委托签名。有关详细信息,请参见协方差和反方差
- C#2.0版引入了匿名方法的概念,它允许代码块作为参数传递,而不是单独定义的方法
委托是一个简单的类,用于指向具有特定签名的方法,本质上成为类型安全的函数指针。委托的目的是在一个方法完成后,以结构化的方式促进对另一个(或多个)方法的回调 虽然可以创建一组广泛的代码来执行此功能,但您不需要太多。您可以使用委托
创建委托很容易。使用“delegate”关键字将类标识为委托。然后指定类型的签名。假设您想编写一个过程,在某个区间[a,b]上积分某个实值函数f(x)。假设我们想使用三点高斯方法来实现这一点(当然,任何方法都可以) 理想情况下,我们需要的功能如下所示:
// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
double res = 0;
// compute result
// ...
return res;
}
所以我们可以传入任何被积函数,f,并得到它在闭区间上的定积分
被积函数应该是什么类型
没有代表
好的,如果没有委托,我们将需要某种带有单个方法的接口,比如eval
,声明如下:
// Interface describing real-valued functions of one variable.
interface Integrand {
double eval(double x);
}
// Some function
class MyFunc1 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// Some other function
class MyFunc2 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// etc
double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);
然后我们需要创建一系列实现该接口的类,如下所示:
// Interface describing real-valued functions of one variable.
interface Integrand {
double eval(double x);
}
// Some function
class MyFunc1 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// Some other function
class MyFunc2 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// etc
double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);
然后要在Gauss3方法中使用它们,我们需要如下调用它:
// Interface describing real-valued functions of one variable.
interface Integrand {
double eval(double x);
}
// Some function
class MyFunc1 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// Some other function
class MyFunc2 : Integrand {
public double eval(double x) {
return /* some_result */ ;
}
}
// etc
double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);
Gauss3需要执行如下操作:
static double Gauss3(Integrand f, double a, double b, int n) {
// Use the integrand passed in:
f.eval(x);
}
因此,我们需要做所有这些,只是为了使用Guass3
中的任意函数
与代表一起
现在我们可以根据该原型定义一些静态(或非静态)函数:
class Program {
public delegate double Integrand(double x);
// Define implementations to above delegate
// with similar input and output types
static double MyFunc1(double x) { /* ... */ }
static double MyFunc2(double x) { /* ... */ }
// ... etc ...
public static double Gauss3(Integrand f, ...) {
// Now just call the function naturally, no f.eval() stuff.
double a = f(x);
// ...
}
// Let's use it
static void Main() {
// Just pass the function in naturally (well, its reference).
double res = Gauss3(MyFunc1, a, b, n);
double res = Gauss3(MyFunc2, a, b, n);
}
}
没有接口,没有笨重的.eval东西,没有对象实例化,只有简单的函数指针式用法,用于简单的任务
当然,委托不仅仅是隐藏的函数指针,但这是一个单独的问题(函数链接和事件)。委托是对方法的引用。虽然对象可以很容易地作为参数发送到方法、构造函数或其他任何东西中,但方法有点棘手。但偶尔您可能会觉得需要将一个方法作为参数发送给另一个方法,这时您需要委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DelegateApp {
/// <summary>
/// A class to define a person
/// </summary>
public class Person {
public string Name { get; set; }
public int Age { get; set; }
}
class Program {
//Our delegate
public delegate bool FilterDelegate(Person p);
static void Main(string[] args) {
//Create 4 Person objects
Person p1 = new Person() { Name = "John", Age = 41 };
Person p2 = new Person() { Name = "Jane", Age = 69 };
Person p3 = new Person() { Name = "Jake", Age = 12 };
Person p4 = new Person() { Name = "Jessie", Age = 25 };
//Create a list of Person objects and fill it
List<Person> people = new List<Person>() { p1, p2, p3, p4 };
//Invoke DisplayPeople using appropriate delegate
DisplayPeople("Children:", people, IsChild);
DisplayPeople("Adults:", people, IsAdult);
DisplayPeople("Seniors:", people, IsSenior);
Console.Read();
}
/// <summary>
/// A method to filter out the people you need
/// </summary>
/// <param name="people">A list of people</param>
/// <param name="filter">A filter</param>
/// <returns>A filtered list</returns>
static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
Console.WriteLine(title);
foreach (Person p in people) {
if (filter(p)) {
Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
}
}
Console.Write("\n\n");
}
//==========FILTERS===================
static bool IsChild(Person p) {
return p.Age < 18;
}
static bool IsAdult(Person p) {
return p.Age >= 18;
}
static bool IsSenior(Person p) {
return p.Age >= 65;
}
}
}
我只是想了解一下这些,所以我将与大家分享一个例子,因为你们已经有了描述,但目前我看到的一个优点是避开了循环引用样式的警告,其中不能有两个项目相互引用 假设应用程序下载了一个XML,然后将XML保存到数据库中 我在这里有两个项目来构建我的解决方案:FTP和一个SaveDatabase 因此,我们的应用程序首先查找任何下载并下载文件,然后调用SaveDatabase项目 现在,我们的应用程序需要在文件保存到数据库时通过上传带有元数据的文件通知FTP站点(忽略原因,这是FTP站点所有者的请求)。问题是在什么时候,如何解决?我们需要一个名为NotifyFtpComplete()的新方法,但它也应该保存在我们的哪个项目中—FTP还是SaveDatabase?从逻辑上讲,代码应该