如何以正确的方式使用代表/了解代表 - c#

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

我试图理解像Delegate这样的主题,目前我的观点不多,我需要澄清一下。
我在互联网上发现了许多不同的信息来描述如何使用它,但是对我来说,理解这个主题有点复杂。

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

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

全部描述如下

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

还有一个关于DELEGATE的问题-在控制台C#应用程序中,什么地方最好用DELEGATE放置代码,我可以在使用的命名空间的任何位置创建它,如下所示。

但是,也许不仅对控制台应用程序而且对于WinForms,WPF等都放置委托的一些建议/要求。

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

编辑

namespace SimpleCSharpApp
{
   delegate void myDelagate ();
}

参考方案

呵呵..你搞砸了。在我看到VS的屏幕快照的“ delegate”声明下带有红色下划线的屏幕截图之前,我还不太了解您要解决的问题。

首先,暂时忽略public void delegate zczcxxzc行。这有点特别。首先,让我们看一些标准类型的委托。

最基本的两个是:

System.Action
System.Func

两者都是通用的,并且第一次看它们的签名,它们似乎过于复杂。但是,它们确实非常简单。

对于初学者,让我们将其限制为裸,无参数的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”保存关于“可调用的东西”或使用某些术语“可调用的东西”的信息。

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

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

最后,我创建一个记住“ myFunction3”的“ Action”类型的委托。然后,我调用/调用该委托-它导致调用已记住的函数,并且什么也没有发生-只是因为目标函数什么都不做。

请注意,我故意说“创建了代表”。每次执行new Action时,都会创建一个新的委托。 “委托”只是一个对象,例如字符串“ foo”或float [] {1.2,4.5}。

另外,请注意,此处使用的创建委托的完整语法为new Action(...)。就像创建任何对象一样-新+类型名+构造参数。另一个代表“委托”只是一个对象。

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

但是,什么是“动作”? System.Action是一个类。像字符串,或套接字或WebClient。这里没什么特别的。因此,我们有一个“类”,其对象可以记住应调用的功能。凉。

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

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

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!

好的,在其他类中创建静态函数的委托很容易-只需确切地说出什么类是什么。再次与调用类似,但没有括号。

但是,如果您尝试取消对非静态方法的引用的注释,则它将无法编译。看GuineaPig.Well-很明显。它不是静态的,需要针对对象而不是类进行调用。由于同样的原因,无法创建委托。让我们修复一下:

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替换classname。语法保留下来:就像调用一样,但没有括号。但是,关于“方法”与“功能”的大惊小怪的是什么。

代表不仅可以存储要调用的“什么方法”,还可以存储调用它们的对象。

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

现在,这是使用普通函数指针无法做到的。 (尽管可以使用非常智能的函数指针,但是,让我们忽略它)。

注意如何在“ pig#2”上调用“ Well”这一事实是如何存储在委托对象中的。 “ myPiggie2”变量无关紧要。我可以取消它。代表记得目标和方法。

System.Action只是家族中的一员。这是最简单的,没有参数,没有返回。.但是,它们很多,它们可以获取参数(Action<string, int>),可以返回值(Func<int>)或两者都返回(Func<string,int>)。然而,经常用Func<int,float,string,int,int,bool,decimal>来表达是有点……晦涩的。

好。让我们终于弄明白所有这些胡言乱语。抱歉,如果您知道所有这些,但我想澄清一下。

“委托”就是关于记住“目标”和“方法”的全部。实际上,如果您曾经在调试器中检查委托(或检查Intellisense在“点”之后的内容),您将看到两个属性,Target和Method。他们就是他们名字的意思。

假设您要创建自己的委托类型。不会被称为Func<int,int,int,bool,bool,Zonk,string>的类型,而是“ MyStudentFilteringDelegate”。

现在,最重要的是,在C#中,您无法轻松获取函数的&(地址),也无法重载operator()。这导致您无法编写自己的类似于委托的类。

您不能只写:

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

     // other code
}

因为,即使您实际上设法遵循了这个想法,但最终还是会发现:

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

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

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

现在回想一下我要求您暂时忘记的public void delegate xxx下的红色下划线。

public bool delegate MyStudentFilteringDelegate( Student stud );

此行不会创建任何委托。该行定义了委托的类型。它与Func<Student,bool>完全相同,带有自定义名称。 *)

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

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

因此它是一个类,因此您现在可以创建一个委托对象:

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

由于该类是特殊的,由编译器生成的类,因此它可能会破坏规则。它的'call'/'invoke'操作符已重载,因此您可以“调用”委托,因为它是一种方法。

请注意,尽管有奇怪的表示法:

public bool delegate MyStudentFilteringDelegate( Student stud );

MyStudentFilteringDelegate是一个类,就像Action或Func或String或WebClient一样。 delegate关键字只是一个标记,编译器可以知道该标记应对该行应用哪种转换以生成适当的“委托类型”(类)。

现在,要实际回答您的其他问题:

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

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

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。就像正常情况一样。

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

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

当回复有时是一个对象有时是一个数组时,如何在使用改造时解析JSON回复? - java

我正在使用Retrofit来获取JSON答复。这是我实施的一部分-@GET("/api/report/list") Observable<Bills> listBill(@Query("employee_id") String employeeID); 而条例草案类是-public static class…

改造正在返回一个空的响应主体 - java

我正在尝试使用Retrofit和Gson解析一些JSON。但是,我得到的响应机构是空的。当我尝试从对象中打印信息时,出现NullPointerException。我确保URL正确,并且我也确保POJO也正确。我正在使用jsonschema2pojo来帮助创建POJO类。这是我要解析的JSON{ "?xml": { "@versi…

每个文件合并后添加换行 - python

我有很多类似以下内容的JSON文件:例如。1.json{"name": "one", "description": "testDescription...", "comment": ""} test.json{"name"…

您如何在列表内部调用一个字符串位置? - python

我一直在做迷宫游戏。我首先决定制作一个迷你教程。游戏开发才刚刚开始,现在我正在尝试使其向上发展。我正在尝试更改PlayerAre变量,但是它不起作用。我试过放在列表内和列表外。maze = ["o","*","*","*","*","*",…

Mongo汇总 - javascript

我的收藏中有以下文件{ "_id": ObjectId("54490b8104f7142f22ecc97f"), "title": "Sample1", "slug": "samplenews", "cat": …