如何改变现有的事件处理程序的名称? [英] How to change the name of an existing event handler?

查看:165
本文介绍了如何改变现有的事件处理程序的名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在的WinForms,如果你改变了方法名称(例如的button1_Click )以别的东西,它不工作了。



我觉得这是奇怪,因为在控制台应用程序,据我记得你希望你能说出的方法。我试图了解发生了什么。



我怎样才能改变这些方法的名称(如的button1_Click )?


解决方案

这个问题是在哪里重命名方法使形式的情况下设计师停止工作。虽然我已经覆盖(所有我能想到的)事件是如何在一般的作品。






发生了什么事?



您体验什么是窗体设计器的神器。



当然,你可以有任何方法名称你想,这件事是窗体设计器是这些方法结合幕后的事件,如果你改变了方法名的形式设计师它链接到它不再工作在事件发生后(不能找到方法)。






给予适当的名字你的事件处理程序



在可视工作室,看对象的属性要绑定一个事件,然后选择事件(在面板的顶部):





有,你会看到一个列表的事件标签可用的事件,你将能够结合现有的方法或键入名称为一个新的:








如果我有已经完蛋了,我该如何解决这个问题?



如果你的设计师是不是因为这个出现你必须编辑由设计器生成的代码文件。由设计器生成的文件的格式,然后按了.Designer.cs (例如的名称: Form1.Designer.cs ),您可以与您的解决方案资源管理器中找到它:



查找设计器生成的代码文件



注意的:你可能需要展开子树的形式创建,揭示文件



有,你会发现一个行看起来是这样的:

  this.button1.Click + =新System.EventHandler(this.button1_Click); 

和Visual Studio会告诉你,的button1_Click 没有定义。您可以编辑有方法的名称的新名称,或删除行再次拥有设计师的工作,并绑定一个新方法。






重命名现有的方法,无需麻烦



您可以召唤重命名对话框。这可以通过几种方式来完成:




  • 从菜单:修改 - > 重构 - > 重命名

  • 将在方法的名称上下文菜单:重构 - > 重命名

  • 将光标放在方法名和键入键Alt + Shift + F10 ,然后选择重命名

  • 将光标方法名,然后按 F2



注意:您可以自定义的Visual Studio,上面的菜单和键盘快捷键可以更改



重命名对话框看起来是这样的:





在那里,你可以为法键入一个新名称,并通过这样做的任何参考或致电给该方法withing加载的项目也将被改变。这包括由窗体设计器生成的代码。






手工绑定的事件处理程序



所有的表单设计确实存在,其有助于编辑表格,代表您编写代码的UI。不要让它愚弄你以为你不能写代码自己。



在事实上,你可以创建你自己的方法,甚至将它们绑定到事件你的表格。我一直在说绑定,因为它更容易在这个层面理解,虽然接受行话是的订阅即可。所以,我们要做的,就是创建一个按钮和订阅的其点击事件。



首先,让我们来看看班级的形式,它看起来是这样的:

 使用System.Windows 。形式; 

命名空间WindowsFormsApplication1
{
公共部分Form1类:表格
{
公共Form1中()
{
的InitializeComponent() ;
}
}
}



注意它说部分,这意味着,有可能是这个类在另一个文件中更多的代码,其实也就是文件 Form1.Designer.cs 其中窗体设计器已添加代码。



第二次通知它调用的InitializeComponent 的构造函数中的一个方法形成。这种方法已经被窗体设计器创建的,它需要初始化您已使用窗体设计器(因此该方法的名称)添加的所有控件和组件的责任。



现在,让我们说,我们要在不窗体设计器添加一个按钮,我们可以做这样的:

  。形式; 

命名空间WindowsFormsApplication1
{
公共部分Form1类:表格
{
私人按钮myButton的;

公共Form1中()
{
的InitializeComponent();

//创建一个新按钮
则myButton =新按钮();

//设置按钮
myButton.Location的性能=新System.Drawing.Point(50,12);
myButton.Size =新System.Drawing.Size(100,23);
myButton.Text =我的按钮;

//将按钮添加到窗体
this.Controls.Add(myButton的);
}
}
}

我们已经创建了一个私有字段名为则myButton 键入按钮,将举行新的按钮。然后构造函数中,我们添加了一些新的行来创建一个新的按钮并将其分配给则myButton ,并给它的位置( 位置),尺寸文本。最后,我们添加了新创建的按钮,将控件的形式。



现在,我们要添加事件处理这个新按钮的Click事件。请记住,这个按钮是不是在窗体设计器,我们,我们将有手来做到这一点。



在为了做到这一点,添加新方法(你可以命名为任何你想要的):

 私人无效WhenClick(对象发件人,发送System.EventArgs)
{
/ *码* /
}

然后将其添加为一个事件该按钮的Click事件(在构造函数中)处理程序:

  //添加事件处理
则myButton。点击+ =新System.EventHandler(WhenClick);

我们不调用

通知 WhenClick 。相反,我们正在对它的引用。



然后,我们正在创造一种新型的代表 System.EventHandler 将包裹参考方法 WhenClick 。我建议了解使用委托



我再说一遍:我们不叫 WhenClick 。如果我们称之为 WhenClick 我们会做这样的事: WhenClick(参数1,参数2)。请注意,这不是我们在这里做,我们还没有添加括号(/*...*/)方法名称后,所以我们没有做。一个方法调用






您也可以使用一些语法上的甜头,使这一切更简单:

 公共Form1中()
{
的InitializeComponent();

//创建一个新的按钮并设置其属性
则myButton =新按钮()
{
位置=新System.Drawing.Point(50,12) ,
大小=新System.Drawing.Size(100,23),
文字=我的按钮
};


//将按钮添加到窗体
this.Controls.Add(myButton的);

//添加事件处理(编译器推断委托类型)
myButton.Click + = WhenClick;
}

您甚至可以使事件处理匿名方法:

  //添加事件处理(编译器推断委托类型)
myButton.Click + =(发件人,E)=>
{
/ *码* /
};



你看到这里是一个的 C#Lambda表达式(MSDN上 Lambda表达式更多信息)。习惯了这些语法,因为你会越来越经常看到他们。






了解事件



您已经看到了这样的代码:

  button1.Click + =的button1_Click; 

正如我告诉你,我们是路过的有引用的委托对象的button1_Click 。但是这还不是所有的事在这里......我们也给它点击



让我们来概括,看看如何委托行为。你可以想想这样的持有方式,或指向如果你喜欢一个函数对象的委托。



要理解这一点,我将介绍一些示例可以作为控制台应用程序运行。第一个显示你可以改变方法的委托点运行时:

  //控制台应用程序示例#1; )
静态无效的主要()
{
Func键< INT,INT> MYFUNC = NULL;

MYFUNC = ADD2;
Console.WriteLine(MYFUNC(7)); //这个打印9
MYFUNC = MultBy2;
Console.WriteLine(MYFUNC(7)); //这将打印14
}

静态INT ADD2(INT X)
{
//此方法增加了2到它的输入
返回X + 2;
}

静态INT MultBy2(INT X)
{
//此方法2
返回X * 2乘以它的输入;
}



通知 MYFUNC 是类型为 Func键< INT,INT> 这是一个的通用委托类型,它接受一个 INT 并返回 INT



还要注意,当我说 MYFUNC = ADD2; 他并没有叫 ADD2 (没有括号出现),它是通过该方法本身的参考。同样是如此 MYFUNC = MultBy2; :他并没有叫 MultBy2 ,我们传递它。

使用通过lambda表达式匿名方法,你可以写等同的代码少行:

  //控制台应用程序实例#1)
静态无效的主要()
{
Func键< INT,INT> MYFUNC = NULL;

//这个匿名方法增加了2到它的输入
MYFUNC = X => X + 2;
Console.WriteLine(MYFUNC(7)); //这个打印9

//这个匿名方法2
MYFUNC = X =>增加了它的输入; X * 2;
Console.WriteLine(MYFUNC(7)); //这个打印14
}

请注意,我们这里有两个匿名方法: X => X + 2 X => X * 2 。第一个( X => X + 2 )等同于方法 ADD2 我们以前和第二个( X => X * 2 ),相当于方法 MultBy2 我们以前



在这个例子中,我希望你能看到相同的委托可以指向沿着时间的不同方法。这是通过具有的可变的指向方法!






有关的第二示例实现的,我将介绍的回调的格局。那是在你传递一个委托作为回调一个共同的模式,那就是:一些被称为回到你身边您所呼叫的代码:

  //控制台应用程序实例#2;)
静态无效的主要()
{
Func键< INT,BOOL>过滤= IsPair;
//与数字
变种数组的数组=新INT [] {1,2,3,4,5,8,9,11,45,99};
PrintFiltered(数组,过滤器);
}

静态布尔IsPair(INT X)
{
//如此对数字
返回X%2 == 0;
}

静态无效PrintFiltered(INT []数组,Func键< INT,布尔过滤器过滤)如果
{
(阵列== NULL)抛出新的ArgumentNullException( 阵);
如果(过滤器== NULL)抛出新的ArgumentNullException(过滤器);
的foreach(数组VAR项)
{
如果(过滤器(项目))
{
Console.WriteLine(项目);
}
}
}



输出:



  2 
4
8

在此代码中,我们正处于一个的变量过滤器是指向方法 IsPair <​​/ code>。我会再和遍地重复这样的:在该行 Func键< INT,BOOL>过滤器= IsPair; 我们不调用方法 IsPair <​​/ code>,相反,我们正在对它的引用



当然,也可以做同样的不声明过滤变量的,可以直接通过该方法参考:

  //控制台应用程序实例#2;)
静态无效的主要()
{
//与数字
变种数组的数组=新INT [] {1,2,3,4,5,8,9,11,45,99};
PrintFiltered(数组,IsPair); //< ---
}

静态布尔IsPair(INT X)
{
//如此对数字
返回X 2% == 0;
}

静态无效PrintFiltered(INT []数组,Func键< INT,布尔过滤器过滤)如果
{
(阵列== NULL)抛出新的ArgumentNullException( 阵);
如果(过滤器== NULL)抛出新的ArgumentNullException(过滤器);
的foreach(数组VAR项)
{
如果(过滤器(项目))
{
Console.WriteLine(项目);
}
}
}



我不能强调不够这个又硬:当我说 PrintFiltered(数组,IsPair); 他并没有叫 IsPair <​​/ code>,它是将它作为一个参数到 PrintFiltered 。在这里,有效地你有一个方法( PrintFiltered ),可以采取另一种方法( IsPair <​​/ code>)作为参考基准。



当然,你可以用它取代 IsPair <​​/ code>匿名方法编写相同的代码:

  //控制台应用程序实例#2;)
静态无效的主要()
{
//使用数组号
变种数组=新INT [] {1,2,3,4,5,8,9,11,45,99};
PrintFiltered(数组,X => X%2 == 0);
}

静态无效PrintFiltered(INT []数组,Func键< INT,布尔过滤器过滤)如果
{
(阵列== NULL)抛出新的ArgumentNullException( 阵);
如果(过滤器== NULL)抛出新的ArgumentNullException(过滤器);
的foreach(数组VAR项)
{
如果(过滤器(项目))
{
Console.WriteLine(项目);
}
}
}



输出:



  2 
4
8

在这个例子中 X => X%2 == 0 是一个匿名方法相当于方法 IsPair <​​/ code>我们以前了。



我们已经成功地过滤该阵列仅显示为一对的数目。您可以轻松地重复使用相同的代码,不同的过滤器。例如,下面的行可用于输出仅是小于10阵列中的项目:



  PrintFiltered(阵列中,x = X的催化剂小于10); 



输出:

  1 
2
3
4
7
8
9

通过这个例子,我想告诉你,你可以采取代表的优势来提高你的代码的可重用性,通过具有变化取决于你通过委托部分






现在是 - 希望 - 我们理解这一点,它认为你可以有代表的名单并不难对象,并呼吁他们在继承:

  //控制台应用程序示例#3)
静态无效的主要()
{
//名单<作用< INT>>是存储类型动作<的对象名单; INT>
//动作< INT>是表示方法的委托,
//接受一个int,但不会返回(示例:void FUNC(INT VAL){/ *代码* /})
变种myDelegates =新的List<作用< INT> ;>();

//我们委托添加到列表
myDelegates.Add(X => Console.WriteLine(X));
myDelegates.Add(X => Console.WriteLine(X + 5));

//我们呼吁他们在演替
的foreach(在myDelegates VAR项)
{
项(74);
}
}



输出:

  74 
79

你可以看到这两个匿名方法( X => Console.WriteLine(X) Console.WriteLine(X + 5))被调用,一前一后...这发生在的foreach 循环中。



现在我们可以完成一个多播委托类似的结果:

  //控制台应用程序示例#3)
静态无效主()
{
//这IA代表......我们没有给它来点方法:
动作< INT> myDelegates = NULL;

//我们方法添加到它
myDelegates + = X => Console.WriteLine(X);
myDelegates + = X => Console.WriteLine(X + 5);

//我们呼吁他们在继承
如果(myDelegates!= NULL)//将是空,如果我们不加方法
{
myDelegates( 74);
}
}



输出:

  74 
79

同样,无论是匿名方法被调用。而这也正是事件是如何工作的。事件的默认实现使用委托的多播包内。自定义实现一个事件可以使用列表或类似的结构来保存代表。



现在,如果该事件仅仅是一个代表列表...这意味着事件是保持所有它想调用的方法的引用。而这也意味着你的可能的从列表中删除的代表(或添加多个)。



如果你想为取消或从事件解除绑定,你可以这样做是这样的:

  this.button1.Click  -  =的button1_Click; 

有关委托对象是更复杂一点,因为你需要一个anoymous方法保持代表在一个变量能够传递回拆卸:

 动作< INT> myDelegates = NULL; 

//创建委托
VAR myDelegate = X => Console.WriteLine(X);

//添加委托
myDelegates + = myDelegate;

// ...

//删除委托
myDelegates - = myDelegate;






你是说一个事件的定制实施<? / H2>

这是否意味着你可以创建自己的事件?是的,是的。如果你想在你的类中的一个发布事件,你可以声明它只是像任何其他成员



这是使用多播委托一个例子:

  //事件的声明:
公共事件的EventHandler MyEvent;

//方法引发事件(又名事件调度程序):
priavet无效Raise_MyEvent()
{
VAR myEvent = MyEvent;
如果(!myEvent = NULL)
{
变种E =新的EventArgs();
myEvent(这一点,E);
}
}

有关自定义实现借这个例子:

  //列表头筹处理程序:
名单,LT;事件处理> myEventHandlers =新的List<&事件处理GT;();

//事件的声明:
事件事件处理MyEvent
{

{
锁(myEventHandlers)
{
myEventHandlers.Add(值);
}
}
拆下
{
锁(myEventHandlers)
{
myEventHandlers.Remove(值);
}
}
}

//方法引发事件(又名事件调度程序):
私人无效Raise_MyEvent()
{
变种E =新的EventArgs();
的foreach(在myEventHandlers VAR项)
{
项(这一点,E);
}
}



但愿这不是很难看,现在你知道事件是如何工作的。唯一的细节应该是 锁定 ,那是因为列表不是线程安全的。你也可以使用一个线程安全的数据结构或不同的锁定机制,我一直在这一个简单。



即使你不写自己的事件,有有几件事情从这里了解:




  1. 事件处理程序连续(默认)执行,所以它是很好的想法,事件处理程序执行速度快(也许推迟到异步操作的辛勤工作),以防堵塞的情况下调度。

  2. 事件调度通常不处理异常(这是可以做到),所以它是好主意,以避免扔在事件处理程序的除外。

  3. 该事件的出版是保持事件处理程序的列表,这是取消的时候,你不需要是个好主意需要事件处理程序了






注释:​​



在查找例如,我发现在C#中的代表 - 尝试往里的系列文章由埃德·古斯曼(的第1部分,的第2部分,的第3部分和的第4部分)很容易阅读 - 虽然有点过时了 - 你应该检查出来。你可能想读href=\"http://msdn.microsoft.com/en-us/library/bb882516.aspx\">匿名函数的演变在C#中了解什么遗漏的把握

一些常用的委托类型在.NET中的buildin包括:




  • 通用​​

    • 动作< *> (即:动作< T> 动作< T1,T2> ...)

    • Func键< *,TResult> (即: Func键< T,TResult> Func键< T1,T2,TResult> ...)

    • 事件处理程序和LT; TEventArgs>

    • 比较和LT ; T>

    • 转换器和LT; TInput,TOutput>


  • 非通用

    • 动作

    • 谓词

    • 事件处理程序

    • 的ThreadStart

    • ParametrizedThreadStart




您可能感兴趣的 LINQ



线程和线程安全,它比委托和事件更广泛的话题,不要急于。明白这一切



如果你想与一般的Lambda表达式和C#一起玩,我建议获得的LINQPad 。本作减少创建新项目,以测试的东西的麻烦。


In WinForms, if you change the method name (for example button1_Click) to something else, it doesn't work any more.

I find this strange because in Console Application as far as I remember you could name the methods as you wish. I'm trying to understanding what's happening.

How can I change the name of these methods (such as button1_Click)?

解决方案

This questions is about the case where renaming a method causes the forms designer to stop working. Although I have covered (all that I can think of) how events works in general.


What happened?

What you experience is an artifact of the forms designer.

Sure, you can have any method name you want, the matter is the forms designer is binding those methods to events behind the scenes, if you change the method name after the forms designer has linked it to the event it will no longer work (it can't find the method).


Giving proper names to your event handlers

In Visual Studio, look at the properties of the object you want to bind an event to, and then select events (on the top of the panel):

There you will see a list the available events and you will be able to bind an existing method or type the name for a new one:


If I have already screwed, How do I fix it?

If your designer is not appearing because of this you will have to edit the code file that is generated by the designer. The file generated by the designer has the name of the form followed by .Designer.cs (for example: Form1.Designer.cs), you can find it with your solution explorer:

Note: You may have to expand the sub-tree created on your form to reveal the file.

There you will find a line that looks something like this:

this.button1.Click += new System.EventHandler(this.button1_Click);

And Visual Studio will tell you that button1_Click is not defined. You can edit there the name of the method to the new name, or remove the line to have the designer work again, and bind a new method.


Renaming an existing method without the hassle

You can summon the Rename dialog. This can be done by several ways:

  • From the Menus: Edit -> Refactor -> Rename
  • By contextual Menu on the name of the Method: Refactor -> Rename
  • Put the cursor on the Method name and type Alt + Shift + F10 and then select Rename
  • Put the cursor on the Method name and press F2

Note: You can customise your Visual Studio, the above menus and keyboard shortcuts may be changed.

The Rename dialog looks like this:

There you can type a new name for the Method, and by doing so any reference or call to that method withing the loaded projects will be changed too. That includes the code generated by the Forms Designer.


Binding an event handler by hand

All that the forms designer does is present a UI that facilitates editing the form and write code on your behalf. Don't let it fool you into thinking that you can't write code yourself.

In fact, you can create your own methods and even bind them to the events of your Form. I have been saying "bind" because it is easier to understand at this level, although the accepted lingo is subscribe. So what we are going to do, is create a button and subscribe to its Click event.

First let's take a look at the class of your form, it looks something like this:

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

Notice it says partial, that means that there could be more code for this class in another file, in fact that is the file Form1.Designer.cs where the forms designer has been adding code.

Second notice it calls a method InitializeComponent inside the constructor of the form. This method has been created by the forms designer and it takes responsibility of initializing all the controls and components you have added using the forms designer (hence the name of the method).

Now, let's say we want to add a button without the forms designer we can do it like this:

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private Button myButton;

        public Form1()
        {
            InitializeComponent();

            // Create a new Button
            myButton = new Button();

            // Set the properties of the Button
            myButton.Location = new System.Drawing.Point(50, 12);
            myButton.Size = new System.Drawing.Size(100, 23);
            myButton.Text = "My Button";

            // Add the Button to the form
            this.Controls.Add(myButton);
        }
    }
}

We have created a private field named myButton of type Button that will hold the new button. Then inside the constructor we add some new lines to create a new Button and assign it to myButton and give it position (Location), Size and Text. And finally we have added the newly created button to the Controls of the form.

Now we want to add an event handler for the Click event on this new button. Remember that this button is not in the forms designer, we we are going to have to do it "by hand".

In order to do so, add the new method (you can named whatever you want):

        private void WhenClick(object sender, System.EventArgs e)
        {
            /* code */
        }

And then add it as an event handler for the Click event of the button (inside the constructor):

            // Add an event handler
            myButton.Click += new System.EventHandler(WhenClick);

Notice we are not calling WhenClick. instead we are making a reference to it.

Then we are creating a new Delegate of type System.EventHandler that will wrap the reference to the method WhenClick. I suggest to learn about Using Delegates.

I repeat: we are not calling WhenClick. If we were to call WhenClick we would do something like this: WhenClick(param1, param2). Please note that this is not what we are doing here, we haven't added parenthesis (/*...*/) after the method name, so we are not doing a method call.


You can also use some syntactic sugar to make all this easier:

        public Form1()
        {
            InitializeComponent();

            // Create a new Button and set its properties
            myButton = new Button()
            {
                Location = new System.Drawing.Point(50, 12),
                Size = new System.Drawing.Size(100, 23),
                Text = "My Button"
            };


            // Add the Button to the form
            this.Controls.Add(myButton);

            // Add an event handler (the compiler infers the delegate type)
            myButton.Click += WhenClick;
        }

You can even make the event handler an anonymous method:

            // Add an event handler (the compiler infers the delegate type)
            myButton.Click += (sender, e) =>
            {
                /* code */ 
            };

What you see here is a C# Lambda expression (more info at MSDN Lambda expressions). Get used to these syntax, because you will see them more and more often.


Understanding events

You have already seen code like this:

button1.Click += button1_Click;

As I told you we are passing a delegate object that has a reference to button1_Click. But that's not all that happens here... we are also giving it to Click.

Let's recapitulate and see how delegates behave. You can think about a delegate like an object that holds a method, or a pointer to a function if you prefer.

To understand this, I'll present some Examples you can run as Console Applications. The first one shows that you can change the method that a delegate points to during runtime:

//Console Application Example #1 ;)
static void Main()
{
    Func<int, int> myfunc = null;

    myfunc = Add2;
    Console.WriteLine(myfunc(7)); // this prints 9
    myfunc = MultBy2;
    Console.WriteLine(myfunc(7)); // this prints 14
}

static int Add2(int x)
{
    // this method adds 2 to its input
    return x + 2;
}

static int MultBy2(int x)
{
    // this method  multiplies its input by 2
    return x * 2;
}

Notice myfunc is typed as Func<int, int> this is a Generic Delegate type that takes an int and returns an int.

Also notice that when I say myfunc = Add2; It is not calling Add2 (there are no parenthesis there), it is passing a reference of the method itself. the same is true for myfunc = MultBy2;: it is not calling MultBy2, we are passing it.

Using anonymous methods via lambda expressions you can write equivalent code is less lines:

//Console Application Example #1 ;)
static void Main()
{
    Func<int, int> myfunc = null;

    // this anonymous method adds 2 to its input
    myfunc = x => x + 2;
    Console.WriteLine(myfunc(7)); // this prints 9

    // this anonymous method  multiplies its input by 2
    myfunc = x => x * 2;
    Console.WriteLine(myfunc(7)); // this prints 14
}

Notice that we have two anonymous methods here: x => x + 2 and x => x * 2. The first one (x => x + 2) is equivalent to the method Add2 we had before, and the second one (x => x * 2) is equivalent to the method MultBy2 we had before.

In this example I want you to see that the same delegate can point to different methods along the time. This is accomplished by having a variable that points to the methods!


For the second example, I'll present the "callback" pattern. That is a common pattern in which you pass a delegate as a "callback", that is: something that will be called "back to you" from the code you are calling:

//Console Application Example #2 ;)
static void Main()
{
    Func<int, bool> filter = IsPair;
    // an array with numbers
    var array = new int[]{1, 2, 3, 4, 5, 8, 9, 11, 45, 99};
    PrintFiltered(array, filter);
}

static bool IsPair(int x)
{
    // true for pair numbers
    return x % 2 == 0;
}

static void PrintFiltered(int[] array, Func<int, bool> filter)
{
    if (array == null) throw new ArgumentNullException("array");
    if (filter== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

Outputs:

2
4
8

In this code we are having a variable filter that points to the method IsPair. I'll repeat this again and and again: in the line Func<int, bool> filter = IsPair; we are not calling the method IsPair, instead we are taking a reference to it.

Of course, it is possible to do the same without declaring the filter variable, you can pass the method reference directly:

//Console Application Example #2 ;)
static void Main()
{
    // an array with numbers
    var array = new int[]{1, 2, 3, 4, 5, 8, 9, 11, 45, 99};
    PrintFiltered(array, IsPair); //<---
}

static bool IsPair(int x)
{
    // true for pair numbers
    return x % 2 == 0;
}

static void PrintFiltered(int[] array, Func<int, bool> filter)
{
    if (array == null) throw new ArgumentNullException("array");
    if (filter== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

I cannot stress this hard enough: When I say PrintFiltered(array, IsPair); it is not calling IsPair, it is passing it as a parameter to PrintFiltered. Here effectively you have a Method (PrintFiltered) that can take a reference to another method (IsPair) as a reference.

Of course you can write the same code using an anonymous method that replaces IsPair:

//Console Application Example #2 ;)
static void Main()
{
    // an array with numbers
    var array = new int[]{1, 2, 3, 4, 5, 8, 9, 11, 45, 99};
    PrintFiltered(array, x => x % 2 == 0);
}

static void PrintFiltered(int[] array, Func<int, bool> filter)
{
    if (array == null) throw new ArgumentNullException("array");
    if (filter== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

Outputs:

2
4
8

In this example x => x % 2 == 0 is an anonymous method that is equivalent to the method IsPair we had before.

We have successfully filtered the array to only show the numbers that are pair. You can easily reuse the same code for a different filter. For example, the following line can be used to output only the items in the array that are less than 10:

    PrintFiltered(array, x => x < 10);

Outputs:

1
2
3
4
7
8
9

With this example I want to show you that you can take advantage of the delegates to improve the reusability of your code, by having parts that change depending on the delegate you pass.


Now that - hopefully - we understand this, it is not hard to think that you could have a list of Delegate objects, and call them in succession:

//Console Application Example #3 ;)
static void Main()
{
    // List<Action<int>> is a List that stores objects of Type Action<int>
    // Action<int> is a Delegate that represents methods that
    //  takes an int but does not return (example: void func(int val){/*code*/})
    var myDelegates = new List<Action<int>>();

    // We add delegates to the list
    myDelegates.Add(x => Console.WriteLine(x));
    myDelegates.Add(x => Console.WriteLine(x + 5));

    // And we call them in succesion
    foreach (var item in myDelegates)
    {
        item(74);
    }
}

Outputs:

74
79

You can see both anonymous methods (x => Console.WriteLine(x) and Console.WriteLine(x + 5)) has been called, one after the other... this happens inside the foreach loop.

Now, we can accomplish similar results with a multicast delegate:

//Console Application Example #3 ;)
static void Main()
{
    // This ia delegate... we haven't give it a method to point to:
    Action<int> myDelegates = null;

    // We add methods to it
    myDelegates += x => Console.WriteLine(x);
    myDelegates += x => Console.WriteLine(x + 5);

    // And we call them in succession
    if (myDelegates != null) // Will be null if we don't add methods
    {
        myDelegates(74);
    }
}

Outputs:

74
79

Again, both anonymous methods has been called. And this is exactly how Events work. The default implementation of an Event uses a multicast delegated wrapped inside. Custom implementation of an event may use a list or similar structure to hold the delegates.

Now, if the event is just a list of delegates... that means the event is keeping a reference to all the methods it wants to call. And it also means that you could remove delegates from the list (or add more than one).

If you want to unsubscribe or unbind from an event, you can do so like this:

this.button1.Click -= button1_Click;

For a delegate object to an anoymous method it is a little bit more complicated, because you will need to keep the delegate in a variable to able to pass it back for removal:

Action<int> myDelegates = null;

// Create the delegate 
var myDelegate = x => Console.WriteLine(x);

// Add the delegate
myDelegates += myDelegate;

// ...

// Remove the delegate
myDelegates -= myDelegate;


Did you say Custom Implementation of an Event?

Does that means you can create your own events? Yes, and yes. If you want to publish an event in one of your classes you can declare it just like any other member.

This is an example that uses a multicast delegate:

// Event declaration:
public event EventHandler MyEvent;

// Method to raise the event (aka event dispatcher):
priavet void Raise_MyEvent()
{
    var myEvent = MyEvent;
    if (myEvent != null)
    {
        var e = new EventArgs();
        myEvent(this, e);
    }
}

For a custom implementation take this example:

// List to hold the event handlers:
List<EventHandler> myEventHandlers = new List<EventHandler>();

// Event declaration:
event EventHandler MyEvent
{
    add
    {
        lock (myEventHandlers)
        {
            myEventHandlers.Add(value);
        }
    }
    remove
    {
        lock (myEventHandlers)
        {
            myEventHandlers.Remove(value);
        }
    }
}

// Method to raise the event (aka event dispatcher):
private void Raise_MyEvent()
{
    var e = new EventArgs();
    foreach (var item in myEventHandlers)
    {
        item(this, e);
    }
}

Hopefully this not hard to read, now that you know how events work. The only details should be lock, it is there because List is not thread-safe. You could also use a thread-safe data structure or a different locking mechanism, I kept this one for simplicity.

Even if you don't write your own events, there are a few things to learn from here:

  1. Event handlers execute consecutively (by default), so it is good idea that event handlers execute fast (maybe defer to async operations for the hard work) to prevent "clogging" the event dispatcher.
  2. The event dispatcher usually don't handle exceptions (it can be done), so it is good idea to avoid throwing excepting in event handlers.
  3. The "publisher" of the event is keeping a list of the event handlers, it is a good idea to unsubscribe when you don't need need the event handler anymore.


Notes:

While looking for example I found the series of articles "Delegates in C# - Attempt to Look Inside" by Ed Guzman (Part 1, Part 2, Part 3 and Part 4) very easy to read - although a bit outdated - you should check them out. You may want to read the Evolution of Anonymous Functions in C# for a grasp of what's missing.

Some commonly used Delegate Types buildin in .NET include:

  • Generic
    • Action<*> (that is: Action<T>, Action<T1, T2> ...)
    • Func<*, TResult> (that is: Func<T, TResult>, Func<T1, T2, TResult> ...)
    • EventHandler<TEventArgs>
    • Comparison<T>
    • Converter<TInput, TOutput>
  • Non Generic
    • Action
    • Predicate
    • EventHandler
    • ThreadStart
    • ParametrizedThreadStart

You may be interested in LINQ.

Threading and thread-safety it a even broader topic than delegates and events, don't rush to understand it all.

If you want play along with Lambda Expression and C# in general, I suggest to get a copy of LINQPad. This make reduce the hassle of creating new project to test things.

这篇关于如何改变现有的事件处理程序的名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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