开发者

Raising events vs direct method calls differences

Raising an event, will call its event handler. eg http://msdn.microsoft.com/en-us/library/aa645739%28VS.71%29.aspx

What is the difference between using the events mechanism 开发者_开发知识库and direct calls to other methods (eg if a condition is met in method A(), call B() )?

And what is the difference between consuming and raising events?

Thanks


The difference is this:

Method call = "Do this specific thing"

Event raise = "If anyone is listening and cares, this thing just happened."

It is central to Separation of Concerns and reusability. A button is not a reusable component if clicking it calls a specific method. But if it simply "announces" to the program it was clicked, and interested parties are responsible for subscribing themselves to that, it is infinitely reusable.

The underlying technical implementation of how this is accomplished (via delegate) is irrelevant.


Raising an event, will call its event handler

That started off wrong. There could be no event handler. Or many. You don't know. Which is the major difference from calling a method directly. Look up "observer pattern" in your favorite design patterns book.


What is the difference between using the events mechanism and direct calls to other methods (eg if a condition is met in method A(), call B() )?

Business logic wise there is no difference between the two. What I mean by this is that you can accomplish the same task each way. It's just a different way of going about it. The real difference is the amount of work you have to do to handle the notification of other modules.

With raising an event, you are essentially saying "Hey, something has occurred any piece of code which has signed up to be notified when this happened, let them know. Which modules that get notified is not my concern, because I am assuming that (at runtime) all modules that need to know are setup for notification."

With calling each method directly, you are making the decision that you are going to tell this (or these) modules, and only these, that something has occurred. You are making that assertion that no matter what the states of these modules are in isn't important and they need to know this event happened.

Both are correct for different situations. Event notifications are more dynamic. Different modules can register and de-register for notifications. Direct method calls are more static. A certain set of objects (or modules etc.) are absolutely going to be notified (barring exceptions of course) that something happened, but only these are going to be notified.


Raising an event (or Invoking, to use the term from your link) means you are sending the event to all consumers. For example, a window can raise an event when it is clicked with the mouse.

Consuming an event means that you are receiving and processing the event from whoever sent it. For example, you might want to know when the window is clicked by the mouse.

If you have only one consumer, then you could accomplish something similar by just supplying a callback directly:

// 'Event' type:
delegate void DelMyEvent();
// consumer:
class Consumer
{
    Producer _theProducer;
    void RegisterForNotification()
    {
       _theProducer.OnMyEvent = new DelMyEvent(OnMyEvent);
    }
    void OnMyEvent() { }
}
// producer:
class Producer
{
   public DelMyEvent OnMyEvent;
   void SendNotification()
   {
      if( OnMyEvent != null ) OnMyEvent();
   }
}

The event mechanism cleans this up a bit by preventing the consumer from setting the delegate value directly. Instead it makes the consumer register itself with the += operator. When the first consumer registers, the delegate gets set, and when the second consumer registers, their two callbacks get chained together by Delegate.Combine.


For anyone, who is interested in the performance of event call, I have made this simple benchmark. It shows the differences between calling a method directly, calling it via interface, via delegate and via event, where one handler is attached.

In each scenario the method is called in corresponding way 1 000 000 000 times. Here are (maybe surprising) results:

Delegate call: 23 240 ms - the fastest

Event call: 23 295 ms

Direct call: 23 396 ms

Interface call: 23 716 ms - the slowest

The mesurements were done in release build using C# in .NET4.0.

The code is here:

class Program
{
    static void Main(string[] args)
    {
        TestClass.RunTest();
        Console.ReadLine();
    }
}

interface ITestClass
{
    void TestMethod(object sender, TestEventArgs eventErgs);
}

class TestClass : ITestClass
{
    #region Events

    event EventHandler<TestEventArgs> TestEvent;

    #endregion

    #region Constructor

    public TestClass()
    {
        TestEvent += TestMethod;
    }

    #endregion

    #region Public Methods

    public static void RunTest()
    {
        int testCount = 1000000000; //1 000 000 000

        string format = "{0:### ### ### ##0}";

        #region Direct Call

        Console.WriteLine("Direct call");
        TestClass testClass = new TestClass();

        testClass.TestMethod(testClass, new TestEventArgs(3));

        Stopwatch stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < testCount; ++i)
        {
            testClass.TestMethod(testClass, new TestEventArgs(3));
        }
        stopwatch.Stop();
        Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds));
        Console.WriteLine();

        #endregion

        #region Interface Call

        Console.WriteLine("Interface call");
        ITestClass itestClass = new TestClass();
        itestClass.TestMethod(testClass, new TestEventArgs(3));

        stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < testCount; ++i)
        {
            itestClass.TestMethod(testClass, new TestEventArgs(3));
        }
        stopwatch.Stop();
        Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds));
        Console.WriteLine();

        #endregion

        #region Delegate Call

        Console.WriteLine("Delegate call");
        TestClass delegateTestClass = new TestClass();
        Action<object, TestEventArgs> delegateMethod = delegateTestClass.TestMethod;
        delegateMethod(testClass, new TestEventArgs(3));

        stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < testCount; ++i)
        {
            delegateMethod(testClass, new TestEventArgs(3));
        }
        stopwatch.Stop();
        Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds));
        Console.WriteLine();

        #endregion

        #region Event Call

        Console.WriteLine("Event call");
        TestClass eventTestClast = new TestClass();
        eventTestClast.TestEvent(testClass, new TestEventArgs(3));

        stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < testCount; ++i)
        {
            eventTestClast.TestEvent(testClass, new TestEventArgs(3));
        }
        stopwatch.Stop();
        Console.WriteLine(string.Format(format, stopwatch.ElapsedMilliseconds));
        Console.WriteLine();

        #endregion
    }

    #endregion

    #region ITestClass Members

    public void TestMethod(object sender, TestEventArgs e)
    {
        e.Result = e.Value * 3;
    }

    #endregion
}

class TestEventArgs : EventArgs
{
    public int Value { get; private set; }

    public int Result { get; set; }

    public TestEventArgs(int value)
    {
        Value = value;
    }
}


In addition to the multiple/no subscribers scenarios above, events are also used to reduce code coupling - eg method A() doesn't need to know anything about about method B() at compile time. This enables better separation of concerns and less fragile code.

In the wild, you're more likely to see events used in framework and UI code, whereas within the domain logic of an application developers more often use things like Separated Interface and Dependency Injection to decouple code. Recently there has been a bit more discussion in various arenas regarding using events within domain logic, an approach which is cunningly named Domain Events.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜