事件与委托--这篇文章算是讲的浅显明白


Introduction

When I was trying to learn events and delegates, I read a lot of articles to completely understand what they are and how to use them, and now I want to present them all here, everything I learned, most of the things you need to learn.

What are delegates?

Delegate and Event concepts are completely tied together. Delegates are just function pointers, That is, they hold references to functions.

Delegate is a class. When you create an instance of it, you pass in the function name (as a parameter for delegate's constructor) to which this delegate will refer.

Every delegate has a signature. For example:

Delegate int SomeDelegate(string s, bool b);

is a delegate declaration. When I say this delegate has a signature, I mean that it returns an int type and takes 2 parameters of type string and bool.

I said, when you instantiate delegates, you pass in the function name to which this delegate will refer as its constructor parameter. The important thing to notice is that only functions that have the same signature as the delegate, can be passed as a parameter.

Consider the following function:

private int SomeFunction(string str, bool bln){...}

You can pass this function to SomeDelegate's constructor, because of their similar signatures.

SomeDelegate sd = new SomeDelegate(SomeFunction);

Now, sd refers to SomeFunction, in other words, SomeFunction is registered to sd. If you call sdSomeFunction will be invoked. Keep in mind what I mean by registered functions. Later, we will refer to registered functions.

sd("somestring", true);

Now that you know how to use delegates, let's understand events...

Understanding Events

  • Button is a class, when you click on it, the click event fires.
  • Timer is a class, every millisecond a tick event fires.

Want to understand what's happening? Let's learn through an example:

This is the scenario: we have a class named Counter. This class has a method named CountTo(int countTo, int reachableNum) which starts counting from 0 to countTo, and raises an event named NumberReached whenever it reaches the reachableNum.

Our class has an event: NumberReached. Events are variables of type delegates. I mean, if you want to declare an event, you just declare a variable of type some delegate and put event keyword before your declaration like this:

public event NumberReachedEventHandler NumberReached;

In the above declaration, NumberReachedEventHandler is just a delegate. Maybe it was better to say: NumberReachedDelegate, but notice that Microsoft doesn't say MouseDelegateor PaintDelegate, instead it offers: MouseEventHandler and PaintEventHandler. It's a convention to say NumberReachedEventHandler instead of NumberReachedDelegate. OK? Good!

You see, before we declare our event, we need to define our delegate (our event handler). It could be something like this:

public delegate void NumberReachedEventHandler(object sender, 
    NumberReachedEventArgs e);

As you see, our delegate's name is: NumberReachedEventHandler, and its signature contains a void return value and 2 parameters of type object and NumberReachedEventArgs. If you somewhere want to instantiate this delegate, the function passed in as constructor parameter should have the same signature as this delegate.

Have you ever used PaintEventArgs or MouseEventArgs in your code to determine the position of mouse, where it was moving, or the Graphics property of the object which raised the Paint event? Actually, we provide our data for the user in a class which is derived from EventArgs class. For example, in our example, we want to provide the number which was reached. And here is the class definition:

None.gifpublic class  NumberReachedEventArgs : EventArgs
ExpandedBlockStart.gif {
InBlock.gif    private int _reached;
InBlock.gif    public NumberReachedEventArgs(int num)
ExpandedSubBlockStart.gif    {
InBlock.gif        this._reached = num;
ExpandedSubBlockEnd.gif    }
InBlock.gif    public int ReachedNumber
ExpandedSubBlockStart.gif    {
InBlock.gif        get
ExpandedSubBlockStart.gif        {
InBlock.gif            return _reached;
ExpandedSubBlockEnd.gif        }
ExpandedSubBlockEnd.gif    }
ExpandedBlockEnd.gif}
None.gif


If it wouldn't be necessary to provide the user with any information, we just use the EventArgs class.

Now, every thing is prepared to take a look inside our Counter class:

None.gifnamespace  Events
ExpandedBlockStart.gif {
InBlock.gif    public delegate void NumberReachedEventHandler(object sender, 
InBlock.gif        NumberReachedEventArgs e);
InBlock.gif
ExpandedSubBlockStart.gif    /// <summary>
InBlock.gif    
/// Summary description for Counter.
ExpandedSubBlockEnd.gif    
/// </summary>
InBlock.gif    public class Counter
ExpandedSubBlockStart.gif    {
InBlock.gif        public event NumberReachedEventHandler NumberReached;
InBlock.gif        
InBlock.gif        public Counter()
ExpandedSubBlockStart.gif        {
InBlock.gif            //
InBlock.gif            
// TODO: Add constructor logic here
InBlock.gif            
//
ExpandedSubBlockEnd.gif
        }
InBlock.gif        public void CountTo(int countTo, int reachableNum)
ExpandedSubBlockStart.gif        {
InBlock.gif            if(countTo < reachableNum)
InBlock.gif                throw new ArgumentException(
InBlock.gif                    "reachableNum should be less than countTo");
InBlock.gif            for(int ctr=0;ctr<=countTo;ctr++)
ExpandedSubBlockStart.gif            {
InBlock.gif                if(ctr == reachableNum)
ExpandedSubBlockStart.gif                {
InBlock.gif                    NumberReachedEventArgs e = new NumberReachedEventArgs(
InBlock.gif                        reachableNum);
InBlock.gif                    OnNumberReached(e);
InBlock.gif                    return;//don't count any more
ExpandedSubBlockEnd.gif
                }
ExpandedSubBlockEnd.gif            }
ExpandedSubBlockEnd.gif        }
InBlock.gif
InBlock.gif        protected virtual void OnNumberReached(NumberReachedEventArgs e)
ExpandedSubBlockStart.gif        {
InBlock.gif            if(NumberReached != null)
ExpandedSubBlockStart.gif            {
InBlock.gif                NumberReached(this, e);//Raise the event
ExpandedSubBlockEnd.gif
            }
ExpandedSubBlockEnd.gif        }
ExpandedSubBlockEnd.gif    }
InBlock.gif


In the above code, we raise an event if we reach the desired number. There are a lot of things to consider here:

  • Raising an event is accomplished through calling our event (an instance of some delegate named NumberReachedEventHandler):
    NumberReached(this, e);

    This way, all registered functions will be invoked.

  • We prepare data for registered functions through this:
    NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
  • One question: why do we indirectly call NumberReached(this, e) through OnNumberReached(NumberReachedEventArgs e) method? Why didn't we use the following code?

     

    None.gifif(ctr ==  reachableNum)
    ExpandedBlockStart.gif {
    InBlock.gif    NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
    InBlock.gif    //OnNumberReached(e);
    InBlock.gif
        if(NumberReached != null)
    ExpandedSubBlockStart.gif    {
    InBlock.gif        NumberReached(this, e);//Raise the event
    ExpandedSubBlockEnd.gif
        }
    InBlock.gif    return;//don't count any more
    ExpandedBlockEnd.gif
    }
    None.gif

     

  • Good question! If you want to know why we called indirectly, take another look at OnNumberReached's signature:

    protected virtual void OnNumberReached(NumberReachedEventArgs e)
    • You see, this method is protected, it means it's available for classes which are derived from this class (inheriting classes).
    • This method is also virtual, this means that it could be overridden in a derived class.

    And this is very useful. Suppose you are designing a class which inherits from Counter class. By overriding OnNumberReached method, you can do some additional work in your class before the event gets raised. An example:

     

    None.gifprotected override void  OnNumberReached(NumberReachedEventArgs e)
    ExpandedBlockStart.gif {
    InBlock.gif    //Do additional work
    InBlock.gif
        base.OnNumberReached(e);
    ExpandedBlockEnd.gif}
    None.gif

     

  • Note that if you don't call base.OnNumberReached(e), the event will never be raised! This might be useful when you are inheriting from some class and want to eliminate some of its events! An interesting trick, huh?

    As a real world example, you can just create a new ASP.NET Web Application and take a look inside the code behind generated. As you see, your page inherits fromSystem.Web.UI.Page class. This class has a virtual and protected method name OnInit. You see that InitializeComponent() method is called inside the overridden method as an extra work, and then OnInit(e) is called in the base class:

    ContractedBlock.gifWeb Form Designer generated code
    None.gif

  • Notice that NumberReachedEventHandler delegate is defined outside our class, inside the namespace, visible to all classes.

OK. Now, it's time to practically use our Counter class:

In our sample application, we have 2 textboxes named txtCountTo and txtReachable as follows:

sample application

And here is the event handler for btnRun click event:

 
       
None.gif private  void cmdRun_Click( object sender, System.EventArgs e)
ExpandedBlockStart.gif {
InBlock.gif    if(txtCountTo.Text == "" || txtReachable.Text=="")
InBlock.gif        return;
InBlock.gif    oCounter = new Counter();
InBlock.gif    oCounter.NumberReached += new NumberReachedEventHandler(
InBlock.gif        oCounter_NumberReached);
InBlock.gif    oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), 
InBlock.gif        Convert.ToInt32(txtReachable.Text));
ExpandedBlockEnd.gif}
None.gif
None.gif private  void oCounter_NumberReached( object sender, NumberReachedEventArgs e)
ExpandedBlockStart.gif {
InBlock.gif    MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
ExpandedBlockEnd.gif}

This is the syntax for initiating an event handler for some event:

oCounter.NumberReached += new NumberReachedEventHandler(
    oCounter_NumberReached);

Now, you understand what you are doing here! You are just instantiating an object of type NumberReachedEventHandler delegate (as you do for any other object). Care about the oCounter_NumberReached method signature similarity as I mentioned before.

And notice that we used += instead of simply =.

It's because delegates are special objects that can hold references to more than one object (here, reference to more than one function). For example, if you had another function named oCounter_NumberReached2 with the same signature as oCounter_NumberReached, both of the functions could be referenced this way:

 
       
None.gifoCounter.NumberReached +=  new NumberReachedEventHandler(
None.gif    oCounter_NumberReached);
None.gifoCounter.NumberReached +=  new NumberReachedEventHandler(
None.gif    oCounter_NumberReached2);

Now, after raising an event, both functions will be invoked one after another.

If somewhere in your code, based on conditions, you decided oCounter_NumberReached2 not be invoked anymore on NumberReached event occurrences, you could simply do this:

 
       
None.gifoCounter.NumberReached -=  new NumberReachedEventHandler(
None.gif    oCounter_NumberReached2);

Finally

Don't forget to define these lines in your application's main constructor, instead of cmdRun_Click event handler. I defined them in my button click event handler just for the sake of simplicity! ;-)

None.gifpublic  Form1()
ExpandedBlockStart.gif {
InBlock.gif    //
InBlock.gif    
// Required for Windows Form Designer support
InBlock.gif    
//
InBlock.gif
    InitializeComponent();
InBlock.gif
InBlock.gif    //
InBlock.gif    
// TODO: Add any constructor code after InitializeComponent call
InBlock.gif    
//
InBlock.gif
    oCounter = new Counter();
InBlock.gif    oCounter.NumberReached += new NumberReachedEventHandler(
InBlock.gif        oCounter_NumberReached);
InBlock.gif    oCounter.NumberReached += new NumberReachedEventHandler(
InBlock.gif        oCounter_NumberReached2);
InBlock.gif


The source code provided by this article is as above.

If your vote is less than 5, let me know the reason ;-). I hope you will be satisfied after reading this article!





本文转自斯克迪亚博客园博客,原文链接:http://www.cnblogs.com/sgsoft/archive/2004/08/24/36121.html,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值