如何正确使用委托/理解委托 [英] How to use delegates in correct way / Understanding delegates

查看:24
本文介绍了如何正确使用委托/理解委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 - C#(.Net Framework 4.5,Visual Studio 2012)

use - C# (.Net Framework 4.5, Visual Studio 2012)

我尝试理解像 Delegate 这样的主题,目前我有几点必须为我澄清.我在互联网上找到了很多描述如何使用它的不同信息,但对我来说理解这个主题有点复杂.

I try to understand such theme like Delegate, and currently I have few points, that must be clarified for me. I found a lot of different information in internet that describe how to use it, but it's a little bit complicated to understanding for me this theme.

据我了解,我必须为使用委托做一些事情:

As I understand I must to do few thing for using delegate:

  • 创建一些实体以使用它(需要创建一些委托)
  • 声明一个委托类型
  • 创建一些我调用委托的方法
  • 在主类调用委托中,需要使用实体的方法(从第一点开始)

我在下面显示的所有描述

all described I show below

问题 - 我是否正确理解了所有内容,或者我错了 - 请澄清一下.

The question - am I correctly understand all or maybe I'm wrong - please clarify it.

还有另一个关于 DELEGATE 的问题 - 将代码与 DELEGATE 放在哪里更好 - 在控制台 C# 应用程序中,我可以在使用过的命名空间的任何位置创建它 - 如下所示.

Also another question regarding DELEGATE - where is better to put code with DELEGATE - in Console C# application I can create it in any place of used Namespace - as I can show below.

但也许有一些建议/要求不仅为控制台应用程序而且为 WinForms、WPF 等放置委托.

But maybe there is some recommendation/requirements for placing delegate not only for Console application but also for WinForms, WPF etc.

这个主题对我来说是新的,我花了一天的时间来理解它,但仍然有点(或更多)对此感到困惑,最后创建这篇文章以获得更好和更清晰的理解.认为这是非常强大的东西.

This theme is new for me and I spend a day for understanding it, but still a little bit (or a more) confused with this, finally create this post for better and clear understanding. think it's very powerfull stuff.

编辑

namespace SimpleCSharpApp
{
   delegate void myDelagate ();
}

推荐答案

Ho-ho.. 你搞砸了.直到我看到 VS 的屏幕截图在代表"声明下带有红色下划线,我才完全理解您要说明的问题.

Ho-ho.. you've got something messed up. I didn't quite grasp what problem you are trying to state until I saw the screenshot from VS with red underline under "delegate" declaration.

首先,暂时忘记 public void delegate zczcxxzc 行.它有点特别.首先,让我们看看一些标准的 *) 代表.

First, forget about the public void delegate zczcxxzc line for a moment. It is somewhat special. First, let's see some standard kinds *) of delegates.

最基本的两个是:

  • System.Action
  • System.Func

两者都是通用的,第一次查看它们的签名,它们可能看起来过于复杂.但是,它们真的很简单.

Both are generic and looking at their signature for the first time, they may seem overly complex. But, they are really very simple.

对于初学者,让我们限制在裸露的、无参数的System.Action.

For starters, let's limit to bare, parameterless, System.Action.

private static void myFunction1() { Console.WriteLine("Hello!"); }
private static void myFunction2() { Console.WriteLine("Bye!"); }
private static void myFunction0() { return; }

... // in some function, i.e. Main()
Action myDelegate = null;

myDelegate = new Action( myFunction1 );
myDelegate(); // writes "Hello!"

myDelegate = new Action( myFunction2 );
myDelegate(); // writes "Bye!"

myDelegate = new Action( myFunction3 );
myDelegate(); // does "nothing"

就像int"包含一个数字,string" - 文本一样,delegate"包含有关可调用的东西"的信息,或者,使用一些术语,可调用的东西".

Just like "int" holds a number, "string" - text, a "delegate" holds information about "something callable", or, to use some terminology, "something invokable".

首先,我创建一个记住myFunction1"的Action"类型的委托.然后我调用/调用该委托 - 它导致调用记住的函数.

First, I create a delegate of type "Action" that remembers "myFunction1". Then I invoke/call that delegate - it results in the remembered function being called.

然后,我创建一个记住myFunction2"的Action"类型的委托.然后我调用/调用该委托 - 它导致调用记住的函数.

Then, I create a delegate of type "Action" that remembers "myFunction2". Then I invoke/call that delegate - it results in the remembered function being called.

最后,我创建一个Action"类型的委托,它记住myFunction3".然后我调用/调用该委托 - 它导致调用记住的函数,但什么也没有发生 - 但只是因为目标函数什么也没做.

Finally, I create a delegate of type "Action" that remembers "myFunction3". Then I invoke/call that delegate - it results in the remembered function being called, and nothing happens - but only because the target function did nothing.

请注意,我故意说创建了一个委托".每次执行 new Action 时,都会创建一个新的委托.委托"只是一个对象,例如 String foo" 或 float[] {1.2, 4.5}.

Please note that I deliberately say "created a delegate". Each time a new Action is executed, a new delegate is created. A "delegate" is just an object, like String "foo" or float[] {1.2, 4.5}.

另外,请注意这里使用的创建委托的完整语法是 new Action(...).就像创建任何对象一样 - new + typename + 构造参数.另一个迹象表明代表"只是一个对象.

Also, note that the full syntax for creating delegates used here is new Action(...). Just like creating any object - new + typename + construction parameters. Yet another sign that a "delegate" is just an object.

另外需要注意的是,我没有写new Action(myFunction1()).我不想调用该方法并获取其结果并将该结果提供给 Action 的构造函数.我写了 new Action(myFunction1).我将函数本身交给了构造函数.

Another thing to note is that I did not write new Action( myFunction1() ). I did not want to CALL the method and get its result and give that result to the constructor of Action. I wrote new Action( myFunction1 ). I gave the function itself to the constructor.

那么,什么是动作"?System.Action 是一个类.像 String、Socket 或 WebClient.这里没什么特别的.所以,我们有一个类",它的对象可以记住应该调用什么函数.很酷.

But, then, what is an "Action"? System.Action is a class. Like String, or Socket or WebClient. Nothing special here. So, we have a "class" whose objects can remember what-function-should-be-called. Cool.

因此,有些人将委托与函数指针"进行比较.但这并不完全正确.函数指针可以记住要调用的什么函数.代表可以记住要调用的方法.还记得区别吗?在上面的示例中,我特意在每个 myFunction 处编写了 static.这些可以称为无对象/无目标.你只需要他们的名字,你可以从任何地方给他们打电话.要调用它们,一个简单的哑指针就足够了.

Therefore some compare delegates to "function pointers". But that's not fully right. Function pointers can remember what function to call. Delegates can remember what method to call. Remember the difference? In my example above, I deliberately wrote static at each myFunction. Those can be called object-less/target-less. You just need their name and you can call them from anywhere. To call them, a simple dumb pointer would be enough.

现在,代表可以做更多事情.他们可以研究方法.但是需要针对对象调用方法..

Now, delegates can do more. They can work on methods. But methods need to be invoked against an object..

class GuineaPig
{
    public static void Squeak() { Console.WriteLine("Ieek!"); }

    public void Well() { Console.WriteLine("actually"); }
    public void IDontKnow() { Console.WriteLine("what they do"); }
}

GuineaPig.Squeak(); // says 'ieek'

Action myDelegate = null;
myDelegate = new Action( GuineaPig.Squeak );
myDelegate(); // writes "ieek"

// GuineaPig.Well(); // cannot do!
// myDelegate = new Action( GuineaPig.Well ); // cannot do!

好的,在其他类中创建一个静态函数的委托很容易——只需要准确地说出什么函数来自什么类.再次就像调用一样,但没有括号.

Ok, making a delegate to a static function in other class was easy - just had to say exactly what-function-from-what-class. Again just like calling, but without parenthesis.

但是,如果您尝试取消注释对非静态方法的引用,它将无法编译.查看 GuineaPig.Well - 这很明显.它不是静态的,它需要针对 OBJECT 而不是 CLASS 调用.出于同样的原因,无法创建委托.让我们解决这个问题:

But, if you try uncommenting the references to non-static methods, it will not compile. Looking at the GuineaPig.Well - that's obvious. It's not static, it needs to be called against OBJECT, not CLASS. For that very same reason, the delegate could not be created. Let's fix that:

class GuineaPig
{
    public void Well() { Console.WriteLine("actually"); }
    public void IDontKnow() { Console.WriteLine("what they do"); }
}

GuineaPig myPiggie = new GuineaPig();

myPiggie.Well(); // ok! writes "actually"

Action myDelegate = null;
myDelegate = new Action( myPiggie.Well ); // ok!

myDelegate(); // ok! writes "actually".

注意在创建委托期间类名是如何被对象变量替换的.语法被保留:就像调用一样,但没有括号.但是,方法"与函数"有什么大惊小怪的......

Note how classname was replaced with objectvariable during the creation of the delegate. The syntax is preserved: just like calling, but without parens. But, what's all the fuss about "methods" vs "functions"..

委托不仅可以存储要调用的什么方法",还可以存储要调用它们的什么对象.

Delegates can store not only 'what method' to be called, but also what object to call them upon.

class GuineaPig
{
    public string Name;

    public void Well() { Console.WriteLine("I'm " + Name); }
}

GuineaPig myPiggie1 = new GuineaPig { Name = "Bubba" };
GuineaPig myPiggie2 = new GuineaPig { Name = "Lassie" };

Action myDelegate = null;

myDelegate = new Action( myPiggie1.Well );
myDelegate(); // -> Bubba

myDelegate = new Action( myPiggie2.Well );
myDelegate(); // -> Lassie

myPiggie1 = myPiggie2 = null;

myDelegate(); // -> Lassie

现在这是你不能用普通函数指针做的事情.(虽然你可以使用非常智能的函数指针......但是,让我们离开吧).

Now that is something you cannot do with plain function pointers. (Although with very smart function pointers you could.. but, let's leave that).

请注意,在pig#2"上调用Well"这一事实是如何存储在委托对象中的.myPiggie2"变量无关紧要.我可以取消它.代理记住了目标和方法.

Note how the fact that "Well" was to be called on "pig#2" was stored inside the delegate object. The "myPiggie2" variable was irrelevant. I could nullify it. The delegate remembered both target and method.

System.Action 只是其中之一.这是最简单的,没有参数,没有返回.但是有很多,他们可以获取参数(Action<string, int>)他们可以返回值(Func<int>) 或两者兼有 (Func).然而,经常用 Func<int,float,string,int,int,bool,decimal> 说话有点……晦涩难懂.

System.Action is just one of the family. It's the simpliest, no params, no returns.. But there are many of them, they can get parameters (Action<string, int>) they can return values (Func<int>) or both (Func<string,int>). Yet, constantly speaking in terms of Func<int,float,string,int,int,bool,decimal> is somewhat ... obscure.

好的.让我们最终进入所有这些喋喋不休的话题.抱歉,如果您知道所有这些,但我想澄清一下.

Ok. Let's finally get to the point of all this babbling. Sorry if you knew all of that, but I wanted to be clear.

代表"就是要记住目标"和方法".事实上,如果您曾经在调试器中检查委托(或检查 Intellisense 在点"之后所说的内容),您将看到两个属性,Target 和 Method.他们正是他们的名字所代表的.

A "delegate" is all about remembering "target" and "method". In fact, if you ever inspect a delegate in the debugger (or check what Intellisense says after the "dot"), you will see two properties, Target and Method. They are just what their names stand for.

假设您想创建自己的委托类型.一种不会被称为 Func<int,int,int,bool,bool,Zonk,string> 的类型,而是MyStudentFilteringDelegate".

Let's imagine you want to create your own type of a delegate. A type that would not be called Func<int,int,int,bool,bool,Zonk,string>, but rather, "MyStudentFilteringDelegate".

现在,重点是,在 C# 中 你不能轻易地获取函数的 &(address),而且 你不能重载运算符().这会导致您无法编写自己的类委托类.

Now, whole point is, that in C# you cannot take &(address) of a function easily, and also you cannot overload the operator(). This results in you being unable to write your own delegate-like classes.

你不能只写:

class MyStudentFilteringDelegate
{
     public object Target;
     public somethingstrange*  MethodPointer;

     // other code
}

因为,即使您实际上设法遵循了这个想法,在最后的某个地方您也会发现:

because, even if you actually managed to follow that idea, somewhere at the end you would find that:

MyDelegate dd = new MyDelegate ( ... );
dd(); // is just impossible!!!

至少,在当前的 C# 版本 4.5 或 5 中.

At least, in current C# version 4.5 or 5.

您不能重载call"/invoke"运算符,因此您将无法完全实现您自己的自定义命名委托类型.您将永远被困在 Actions 和 Funcs 中.

You just cannot overload the "call"/"invoke" operator, hence you would not be able to fully implement your own, custom-named, delegate type. You'd be stuck at Actions and Funcs for ever.

现在回想一下 public void delegate xxx 下的红色下划线,我让你暂时忘记.

Now recall that red underline under the public void delegate xxx I asked you to temporarily forget.

public bool delegate MyStudentFilteringDelegate( Student stud );

此行不创建任何委托.这一行定义了一个委托类型.它与 Func<Student,bool> 完全相同,具有自定义名称.*)

This line does not create any delegate. This line defines a type of a delegate. It is perfectly the same as Func<Student,bool>, with custom name. *)

实际上,编译器将这一行转换为:

In fact, the compiler converts the line to:

public class MyStudentFilteringDelegate : some_framework_Delegate_class
{
    // object Target {get;} - inherited from base class
    // MethodInfo Method {get;} - inherited from base class, too
}

让它成为一个,这样你现在就可以创建一个delegate对象了:

so that it is a class, and so that you can now create a delegate object:

var dd = new MyStudentFilteringDelegate ( ... ); // like normal class!
dd(); // ok!;

由于类是特殊的,编译器生成的,它可以打破规则.它的 'call'/'invoke' 运算符被重载,因此您可以像方法一样调用"委托.

As the class is special, compiler-generated, it can break rules. It's 'call'/'invoke' operator is overloaded, so you can "call" the delegate as it were a method.

请注意,尽管符号很奇怪:

Please note that despite the strange notation:

public bool delegate MyStudentFilteringDelegate( Student stud );

MyStudentFilteringDelegate 是一个,就像 Action 或 Func 或 String 或 WebClient 一样.delegate 关键字只是一个标记,让编译器知道它应该对该行应用什么转换以生成正确的委托类型"(类).

the MyStudentFilteringDelegate is a class, just like Action or Func or String or WebClient are. The delegate keyword is just a marker for the compiler to know what transformation it should apply to that line to generate a proper "delegate type" (class).

现在,实际回答您的另一个问题:

Now, to actually answer your other question:

委托类型声明的放置位置真的无关紧要.你可以在任何你喜欢的地方写 public void delegate XYZ(...) .就像您可以在任何地方放置类声明一样.

It is really irrelevant where you put the delegate type declaration. You may write public void delegate XYZ(...) anywhere you like. Just as you may place a class declaration just about anywhere.

您可以将类声明放置在默认(无命名空间)范围、某个命名空间或类内部.因此,由于委托类型只是一个类,您还可以在默认(无命名空间)范围、某个命名空间或类内部删除新的委托类型:

You may place class declarations at default(no-namespace) scope, at some namespace, or inside a class. So, as the delegate-type is just a class, you may also delcare new delegate types at default(no-namespace) scope, at some namespace, or inside a class:

public class Xc {}
public void delegate Xd();

namespace WTF {
    public class Xc {}
    public void delegate Xd();

    class Whatever {
        public class Xc {}
        public void delegate Xd();
    }
}

请注意,我完全故意将它们命名为相同.这没有错误.第一个被命名为 ::global.Xc::global.Xd,第二对被命名为 WTF.XcWTF.Xd,最后一对被命名为 WTF.Whatever.XcWTF.Whatever.Xd.就像普通的班级一样.

Note that I completely deliberately named them identically. That's no error. First are named ::global.Xc and ::global.Xd, the second pair is named WTF.Xc and WTF.Xd and the final pair is named WTF.Whatever.Xc and WTF.Whatever.Xd. Just like normal clases.

要决定在哪里放置这些声明,请使用与类相同的规则.IE.如果您将文本处理类放在命名空间 MyApp.Text.Parsing 中,那么与该文本处理相关的所有委托类型也应该位于该命名空间中.但是,即便如此,这纯粹是装饰性的和组织性的.在对您有意义的任何范围内放置/定义它们.

To decide where to place those declarations, use the same rules as you use for classes. Ie. if you place text-processing classes in namespace MyApp.Text.Parsing, then all the delegatetypes related to that text-processing should sit in that namespace too. But, even so, that's purely cosmetical and organizational. Place/define them at whatever scope it makes sense for you.

*) 实际上,从历史上看,情况完全不同.delegate 关键字和编译器技巧比 Action 和 Func 类.在 .Net 2.0 中,Action/Func 不存在.创建/使用委托的唯一方法是定义您自己的新委托类型(或在系统命名空间深处的某个地方找到/猜测一些合适的委托类型).请记住,每个新的委托类型都是一个新类.不能转换为任何其他类,甚至没有类似的外观.它是如此令人沮丧且难以维护,以至于在 .Net 3.5 中,他们最终在框架中包含了通用通用委托类型".从那时起,Action/Func 被越来越多地使用,因为即使它们更难阅读,它们也是……通用的.System.Func<Student,bool> 可以在任何地方"传递,并且您不会遇到'bool delegate StudentFilter() from one library does not match的问题来自另一个的 bool 委托 StudentSelector()`.

*) actually, historically, it was all other way around. The delegate keyword and compiler trick is older than the Action and Func classes. At .Net 2.0, the Action/Func did not exist. The only way to create/use a delegate was to define your own new delegate type (or find/guess some suitable one somewhere deep in the system's namespaces). Keep in mind that every new delegate-type is a new class. Not convertible to any other class, not even similarly-looking. It was so frustratingly tiresome and hard to mantain, that at .Net 3.5 they finally included "general purpose generic delegate types" in the framework. From that point of time, Action/Func are increasingly often used, because even if they are harder to read, they are .. universal. System.Func<Student,bool> can be passed "anywhere", and you don't have a problem that 'bool delegate StudentFilter()from one library does not matchbool delegate StudentSelector()` from anoother one.

这篇关于如何正确使用委托/理解委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆