这个问题已经在这里有一个答案:
- 我在哪里可以使用委托? [关闭] 8个答案
我在C#中相对较新的,和我不知道什么时候适当地使用委托 。 它们被广泛应用于事件的声明,但我当应该我自己的代码中使用它们以及它们为何如此有用吗? 为什么不能用别的东西吗?
我也想知道,当我必须使用委托,我没有其他选择 。
感谢您的帮助!
编辑:我想我已经找到了一个必要使用委托的 位置
这个问题已经在这里有一个答案:
我在C#中相对较新的,和我不知道什么时候适当地使用委托 。 它们被广泛应用于事件的声明,但我当应该我自己的代码中使用它们以及它们为何如此有用吗? 为什么不能用别的东西吗?
我也想知道,当我必须使用委托,我没有其他选择 。
感谢您的帮助!
编辑:我想我已经找到了一个必要使用委托的 位置
我同意一切,是已经说了,只是试图把一些换句话说就可以了。
委托可以被看作是一个占位符/一些方法(一个或多个)。
通过定义一个代表,你说你的类的用户,“ 请随意分配,匹配该签名,以委托任何方法,并会在每个我的委托被调用时,被称为 ”。
典型的应用是当然的事件。 所有OnEventX 委托给用户定义的方法。
代表们提供您的对象的用户定制自己的行为的一些能力是有用的。 大多数时候,你可以用其他的方式来达到同样的目的,我不相信你都不能强迫创建委托。 它只是在某些情况下,最简单的方式得到的东西完成。
委托是对一个方法的参考。 而对象可以很容易地作为参数送入方法,构造或什么的,方法是比较麻烦一些。 但每过一段时间你可能会觉得有必要发送方法作为参数传递给另一个方法,那就是当你需要的代表。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;
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;
}
}
}
假设你想要写一个程序,一些实值函数f(x)在区间的一些整合[A,B]。 假设我们要使用的3点高斯的方法来做到这一点(任何会做,当然)。
理想情况下,我们需要一些功能,看起来像:
// '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;
}
因此,我们可以通过任何Integrand
,f和获得它的定积分在闭区间。
只是要什么类型的Integrand
是什么?
好了,没有代表,我们需要一些类型的接口有一个方法,说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
然后在我们的Gauss3方法使用它们,我们需要如下调用它:
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
。
public delegate double Integrand(double x);
现在,我们可以定义遵守这一原型一些静态的(或没有)的功能:
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的东西,没有对象实例化,只是简单的函数指针一样使用,对于一个简单的任务。
当然,代表们比引擎盖下只是函数指针多,但是这是一个单独的问题(功能链接和事件)。
想声明的是要绕过的代码块时,代表们是非常有用的。 使用通用重试机制时,例如。
伪:
function Retry(Delegate func, int numberOfTimes)
try
{
func.Invoke();
}
catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }
或者当你想要做的代码块的评价晚了,想在这里你有一些函数Transform
动作,并希望有一个BeforeTransform
和AfterTransform
的行动,您可以在转换功能内评估,而不必知道是否BeginTransform
充满,或者它有可能改变什么。
和创建事件处理程序时,当然。 你不想现在来评估代码,但仅在需要时,让你注册在事件发生时可调用的委托。
代表们概述
代表具有以下属性:
- 委托类似于C ++函数指针,但类型安全。
- 委托考虑方法作为参数传递。
- 代表可以用来定义回调方法。
- 代表可以链接在一起; 例如,多种方法可以在单个事件被调用。
- 方法不需要精确匹配委托签名。 欲了解更多信息,请参阅协方差和方差魂斗罗。
- C#版本2.0引入匿名方法,其允许代码块,以代替单独定义的方法的参数传递的概念。
我刚刚去我围绕这些头,所以你已经有了说明,但目前一个好处我看到的是要解决循环引用样式警告,你不能有2个项目,每个引用我给大家介绍一个例子其他。
让我们假设一个应用程序下载的XML,然后保存XML到数据库。
我这里有2个项目,其建设我的解决方案:FTP和SaveDatabase。
所以,我们的应用程序开始通过寻找任何下载和下载文件(S),那么它调用SaveDatabase计划。
现在,我们的应用程序需要当一个文件被上传与元数据文件(忽略为什么,这是从FTP站点的所有者的请求)保存到数据库中,以通知的FTP站点。 问题是在什么时候和怎么样? 我们需要)被称为NotifyFtpComplete(一种新的方法,但在我们的项目应该把它保存得 - FTP或SaveDatabase? 从逻辑上讲,代码应该生活在我们的FTP项目。 但是,这将意味着我们的NotifyFtpComplete将不得不被触发,或将不得不等待,直到保存完成,然后查询数据库,以确保它在那里。 我们需要做的是告诉我们的SaveDatabase项目调用NotifyFtpComplete()方法直接,但是我们不能; 我们会得到一个ciruclar参考,NotifyFtpComplete()是一个私有方法。 太可惜了,这会工作。 好了,就可以了。
在我们的应用程序的代码,我们会通过参数的方法之间,但如果这些参数之一是NotifyFtpComplete方法。 是的,我们通过该方法,所有的代码里面也是这样。 这意味着我们可以在任何时候执行的方法,从任何项目。 那么,这是该委托是什么。 这意味着,我们可以通过NotifyFtpComplete()方法作为参数传递给我们的SaveDatabase()类。 在节省点,它只是执行委托。
看看这个最原始的例子帮助(伪代码)。 我们还假设应用程序与FTP类的开始()方法开始。
class FTP
{
public void Begin()
{
string filePath = DownloadFileFromFtpAndReturnPathName();
SaveDatabase sd = new SaveDatabase();
sd.Begin(filePath, NotifyFtpComplete());
}
private void NotifyFtpComplete()
{
//Code to send file to FTP site
}
}
class SaveDatabase
{
private void Begin(string filePath, delegateType NotifyJobComplete())
{
SaveToTheDatabase(filePath);
//InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class.
NotifyJobComplete.Invoke();
}
}
所以,按照这种解释,我们可以真正现在有了这个控制台应用程序用C#做
using System;
namespace ConsoleApplication1
{
//I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
class Program
{
static void Main(string[] args)
{
//Note, this NotifyDelegate type is defined in the SaveToDatabase project
NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);
SaveToDatabase sd = new SaveToDatabase();
sd.Start(nofityDelegate);
Console.ReadKey();
}
//this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet).
private static void NotifyIfComplete()
{
Console.WriteLine(_notice);
}
private static string _notice = "Notified";
}
public class SaveToDatabase
{
public void Start(NotifyDelegate nd)
{
Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed.");
Console.WriteLine("SaveToDatabase Complete");
Console.WriteLine(" ");
nd.Invoke();
}
}
public delegate void NotifyDelegate();
}
我建议你通过代码,看看当_notice被称为当方法(委托)称为此,我希望,会使事情很清楚。
不过,最后,我们可以通过改变委托类型,包括一个参数就更加有用。
using System.Text;
namespace ConsoleApplication1
{
//I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
class Program
{
static void Main(string[] args)
{
SaveToDatabase sd = new SaveToDatabase();
//Please note, that although NotifyIfComplete() takes a string parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later.
NotifyDelegateWithMessage notifyDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);
sd.Start(notifyDelegateWithMessage );
Console.ReadKey();
}
private static void NotifyIfComplete(string message)
{
Console.WriteLine(message);
}
}
public class SaveToDatabase
{
public void Start(NotifyDelegateWithMessage nd)
{
//To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable.
string message = string.Empty;
if (DateTime.Now.Second > 30)
message = "Saved";
else
message = "Failed";
//It is at this point we pass the parameter to our method.
nd.Invoke(message);
}
}
public delegate void NotifyDelegateWithMessage(string message);
}
我认为代表是匿名接口 。 在很多情况下,当你需要一个接口与一个单一的方法,你可以使用它们,但你不希望定义一个接口的开销。
委托是用来指向具有特定签名的方法,实质上成为一个类型安全的函数指针的简单类。 委托的目的是促进回调到另一种方法(或方法),一个已经完成后,以结构化方式。
虽然它可能可以创建一套广泛的代码来执行此功能,你不需要太多。 您可以使用委托。
创建一个代表是很容易做到。 标识类为与“委托”关键字的委托。 然后指定类型的签名。