小智 287
委托是对方法的引用.尽管对象可以很容易地作为参数发送到方法,构造函数或其他任何方法,但方法有点棘手.但每隔一段时间你就会觉得需要将一个方法作为参数发送到另一个方法,那就是你需要代理的时候.
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*dis 275
我同意已经说过的所有内容,只是试着说一些其他的话.
委托可以被视为/某些方法的占位符.
通过定义委托,您要对您的类的用户说:" 请随意将与此签名匹配的任何方法分配给委托,并且每次调用委托时都会调用它 ".
典型的用途当然是事件.所有OnEventX都委托给用户定义的方法.
代表可以向对象的用户提供一些自定义行为的功能.大多数情况下,您可以使用其他方式来实现相同的目的,我不相信您可能会被迫创建委托.在某些情况下,这是完成任务的最简单方法.
Ale*_*ski 144
假设您要编写一个过程来在某个区间[a,b] 上集成一些实值函数f(x).假设我们想要使用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;
}
Run Code Online (Sandbox Code Playgroud)
因此,我们可以通过在任何Integrand
,˚F,并获得其定积分在闭区间.
应该Integrand
是什么类型的?
好吧,没有代理,我们需要一种具有单一方法的接口,eval
如下声明:
// Interface describing real-valued functions of one variable.
interface Integrand {
double eval(double x);
}
Run Code Online (Sandbox Code Playgroud)
然后我们需要创建一大堆实现此接口的类,如下所示:
// 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
Run Code Online (Sandbox Code Playgroud)
然后在我们的Gauss3方法中使用它们,我们需要调用它如下:
double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);
Run Code Online (Sandbox Code Playgroud)
而Gauss3需要看起来如下所示:
static double Gauss3(Integrand f, double a, double b, int n) {
// Use the integrand passed in:
f.eval(x);
}
Run Code Online (Sandbox Code Playgroud)
所以我们需要做的就是使用我们的任意函数Guass3
.
public delegate double Integrand(double x);
Run Code Online (Sandbox Code Playgroud)
现在我们可以定义一些遵循该原型的静态(或非)函数:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
对于简单的任务,没有接口,没有笨重的.eval东西,没有对象实例化,只是简单的函数指针,如使用.
当然,委托不仅仅是功能指针,而是一个单独的问题(函数链和事件).
Jan*_*oom 29
当想要声明要传递的代码块时,委托非常有用.例如,使用通用重试机制时.
伪:
function Retry(Delegate func, int numberOfTimes)
try
{
func.Invoke();
}
catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }
Run Code Online (Sandbox Code Playgroud)
或者当你想对代码块进行后期评估时,比如你有一些Transform
动作的函数,并希望有一个你可以在你的转换函数中评估BeforeTransform
的AfterTransform
动作,而不必知道它是否BeginTransform
被填充,或者它是什么必须改造.
当然还有创建事件处理程序时.您现在不想评估代码,但仅在需要时才进行评估,因此您可以注册可在事件发生时调用的委托.
Luk*_*kas 21
代表概述
代表具有以下属性:
- 委托类似于C++函数指针,但是类型安全.
- 委托允许方法作为参数传递.
- 代理可用于定义回调方法.
- 代表们可以被链接在一起; 例如,可以在单个事件上调用多个方法.
- 方法不需要完全匹配委托签名.有关更多信息,请参阅协方差和Contra方差.
- C#版本2.0引入匿名方法,其允许代码块,以代替单独定义的方法的参数传递的概念.
Dav*_*ave 21
我只是围绕这些,所以我会分享一个例子,因为你已经有了描述,但目前我看到的一个优点是绕过循环参考样式警告你不能有2个项目引用每个其他.
假设应用程序下载XML,然后将XML保存到数据库.
我在这里有2个项目构建我的解决方案:FTP和SaveDatabase.
因此,我们的应用程序首先查找任何下载并下载文件,然后调用SaveDatabase项目.
现在,我们的应用程序需要通过上传带有Meta数据的文件将文件保存到数据库时通知FTP站点(忽略原因,这是来自FTP站点所有者的请求).问题在于什么时候以及如何?我们需要一个名为NotifyFtpComplete()的新方法,但是我们应该保存哪些项目--FTP或SaveDatabase?从逻辑上讲,代码应该存在于我们的FTP项目中.但是,这意味着必须触发我们的NotifyFtpComplete,或者它必须等到保存完成,然后查询数据库以确保它在那里.我们需要做的是告诉我们的SaveDatabase项目直接调用NotifyFtpComplete()方法,但我们不能; 我们得到一个ciruclar引用,NotifyFtpComplete()是一个私有方法.真可惜,这本来有用.好吧,它可以.
在我们的应用程序代码中,我们会在方法之间传递参数,但是如果其中一个参数是NotifyFtpComplete方法会怎样.是的,我们传递方法,同时包含所有代码.这意味着我们可以在任何项目中执行该方法.嗯,这就是代表的意思.这意味着,我们可以将NotifyFtpComplete()方法作为参数传递给SaveDatabase()类.在它保存的那一刻,它只是执行委托.
看看这个粗略的例子是否有帮助(伪代码).我们还假设应用程序以FTP类的Begin()方法开始.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
因此,有了解释,我们现在可以使用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)
{
/* I shouldn't write to the console from here,
* just for demonstration purposes
*/
Console.WriteLine("SaveToDatabase Complete");
Console.WriteLine(" ");
nd.Invoke();
}
}
public delegate void NotifyDelegate();
}
Run Code Online (Sandbox Code Playgroud)
我建议你逐步完成代码,看看_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 parameter later.
*/
var 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);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
523998 次 |
最近记录: |