如何以正确的方式使用代表/了解代表 [英] How to use delegates in correct way / Understanding delegates

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

问题描述

使用-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放置代码-在Console 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的屏幕快照的"delegate"声明下带有红色下划线的屏幕截图之前,我还不太了解您要解决的问题.

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
  • 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".

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

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.

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

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时,都会创建一个新的委托. 委托"只是一个对象,例如字符串"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(...).就像创建任何对象一样-新+类型名+构造参数.另一个迹象表明委托"只是一个对象.

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!

好吧,在其他类中创建静态函数的委托很容易-只需确切地说出what-function-from-what-c​​lass.再次像调用一样,但没有括号.

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-很明显.它不是静态的,需要针对对象而不是类进行调用.出于同样的原因,无法创建委托.让我们解决这个问题:

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".

请注意,在创建委托期间,如何用objectvariable替换类名.语法保留下来:就像调用一样,但没有括号.但是,关于方法"与功能"的大惊小怪的是什么.

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<string,int>).但是,经常用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#中,您不能轻松获取函数的&(地址),而且您不能重载operator().这导致您无法编写自己的类似于委托的类.

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
}

,使其成为,因此您现在可以创建委托对象:

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(...).就像您可以在任何地方放置 class声明一样.

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>可以随处传递",并且您不会出现另一个对象布尔委托StudentFilter()from one library does not match布尔委托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天全站免登陆