Effective C# 原則35:選擇重寫函數而不是使用事件控制代碼(譯)

來源:互聯網
上載者:User

Effective C# 原則35:選擇重寫函數而不是使用事件控制代碼
Item 35: Prefer Overrides to Event Handlers

很多.Net類提供了兩種不同的方法來控制一些系統的事件。那就是,要麼添加一個事件控制代碼;要麼重寫基類的虛函數。為什麼要提供兩個方法來完成同樣的事情呢?其實很簡單,那就是因為不同的情況下要調用為的方法。在衍生類別的內部,你應該總是重寫虛函數。而對於你的使用者,則應該限制他們只使用控制代碼來響應一些不相關的對象上的事件。

例如你很了一個很不錯的Windows應用程式,它要響應滑鼠點下的事件。在你的表單類中,你可以選擇重寫OnMouseDown()方法:

public class MyForm : Form
{

  // Other code elided.

  protected override void OnMouseDown(
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
    // *almost always* call base class to let
    // other event handlers process message.
    // Users of your class expect it.
    base.OnMouseDown( e );
  }
}

或者你可以添加一個事件控制代碼:

public class MyForm : Form
{

  // Other code elided.

  public MyForm( )
  {
    this.MouseDown += new
      MouseEventHandler( this.MouseDownHandler );
  }

  private void MouseDownHandler( object sender,
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
  }
}

前面一些方法要好一些,如果在事件鏈上有一個控制代碼拋出了一個異常,那麼其它的控制代碼都不會再被調用(參見原則21)。一些“病態”的代碼會阻止系統呼叫事件上的控制代碼。通過重寫受保護的虛函數,你的控制控制代碼會就先執行。基類上的虛函數有責任調用詳細事件上的所有添加的控制代碼。這就是說,如果你希望事件上的控制代碼被調用(而且這是你最想完成的),你就必須調用基類。而在一些罕見的類中,你希望取代基類中的預設事件行為,這樣可以讓事件上的控制代碼都不被執行。你不去保證所所的事件控制代碼都將被調用,那是因為一些“病態”事件控制代碼可能會引發一些異常,但你可以保證你衍生類別的行為是正確的。

使用重載比添加事件控制代碼更高效。我已經在原則22中告訴過你,System.Windows.Forms.Control類是如何世故的使用任命機制來儲存事件控制代碼,然後映射恰當的控制代碼到詳細的事件上。這種事件機制要花上更多的處理器時間,那是因為它必須檢測事件,看它是否有事件控制代碼添加在上面。如果有,它就必須迭代整個調用鏈表。方法鏈表中的每個方法都必須調用。斷定有哪些事件控制代碼在那裡,還要對它們進行運行時迭代,這與只調用一個虛函數來說,要花上更多的執行時間。

如果這還不足以讓你決定使用重載,那就再看看這一原則一開始的鏈表。那一個更清楚?如果重載虛函數,當你在維護這個表單時,只有一個函數要檢查和修改。而事件機制則有兩個地方要維護:一個就是事件控制代碼,另一就是事件控制代碼上的函數。任何一個都可能出現失敗。就一個函數更簡單一些。

OK,我已經給出了所有要求使用重載而不是事件控制代碼的原因。.Net架構的設計者必須要添加事件給某人,對嗎?當然是這樣的。就你我們剩下的內容一個,他們太忙了而沒時間寫一些沒人使用的代碼。重寫只是為衍生類別提供的,其它類必須使用事件機制。例如,你經常添加一個按鈕點擊事件到一個表單上。事件是由按鈕觸發的,但是由表單對象處理著事件。你完全可以在這個類中定義一個使用者的按鈕,而且重寫這個點擊控制代碼,但這對於只是處理一個事件來說花上了太多的代碼。不管怎樣,問題都是交給你自己的類了:你自己定義的按鈕還是在點擊時必須與表單進行通訊。顯然應該用事件來處理。因此,最後,你只不過是建立了一個新類來向表單發送事件(譯註:其實我們完全可以建立這個類不用發事件給表單就可以完成回調的,只是作者習慣的說什麼好就一味的否定其它。但不管怎樣,重寫一個按鈕來重載函數確實不是很值。)。 相對前面一種方法,直接在表單事件添加控制代碼要簡單得多。這也就是為什麼.Net架構的設計者把事件放在表單的最前面。

另一個要使用事件的原因就是,事件是在運行時處理的。使用事件有更大的伸縮性。你可以在一個事件上添加多個控制代碼,這取決於程式的實際環境。假設你寫了一個繪圖程式,根據程式的狀態,滑鼠點下時應該畫一條線,或者這它是要選擇一個對象。當使用者切換功能模式時,你可以切換事件控制代碼。不同的類,有著不同的事件控制代碼,而處理的事件則取決於應用程式的狀態。

最後,對於事件,你可以把多個事件控制代碼掛到同樣的事件上。還是想象同樣的繪圖程式,你可能在MouseDown事件上掛接了多個事件控制代碼。第一個可能是完成詳細的功能,第二個可能是更新狀態條或者更新一些可訪問的不同命令。不同的行為可以在同一事件上響應。

當你有一個衍生類別中只有一個函數處理一個事件時,重載是最好的方法。這更容易維護,今後也會更正確,而且更高效。而應該為其它使用者保留事件。因此,我們應該選擇重寫基類的實現而不是添加事件控制代碼。
=======================
   

Item 35: Prefer Overrides to Event Handlers
Many .NET classes provide two different ways to handle events from the system. You can attach an event handler, or you can override a virtual function in the base class. Why provide two ways of doing the same thing? Because different situations call for different methods, that's why. Inside derived classes, you should always override the virtual function. Limit your use of the event handlers to responding to events in unrelated objects.

You write a nifty Windows application that needs to respond to mouse down events. In your form class, you can choose to override the OnMouseDown() method:

public class MyForm : Form
{

  // Other code elided.

  protected override void OnMouseDown(
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
    // *almost always* call base class to let
    // other event handlers process message.
    // Users of your class expect it.
    base.OnMouseDown( e );
  }
}

 

Or, you could attach an event handler:

public class MyForm : Form
{

  // Other code elided.

  public MyForm( )
  {
    this.MouseDown += new
      MouseEventHandler( this.MouseDownHandler );
  }

  private void MouseDownHandler( object sender,
    MouseEventArgs e )
  {
    try {
      HandleMouseDown( e );
    } catch ( Exception e1 )
    {
      // add specific error handling here.
    }
  }
}

 

The first method is preferred. If an event handler throws an exception, no other handlers in the chain for that event are called (see Item 21). Some other ill-formed code prevents the system from calling your event handler. By overriding the protected virtual function, your handler gets called first. The base class version of the virtual function is responsible for calling any event handlers attached to the particular event. That means that if you want the event handlers called (and you almost always do), you must call the base class. In some rare cases, you will want to replace the default behavior instead of calling the base class version so that none of the event handlers gets called. You can't guarantee that all the event handlers will be called because some ill-formed event handler might throw an exception, but you can guarantee that your derived class's behavior is correct.

Using the override is more efficient than attaching the event handler. I showed you in Item 22 how the System.Windows.Forms.Control class uses a sophisticated collection mechanism to store event handlers and map the appropriate handler to a particular event. The event-handling mechanism takes more work for the processor because it must examine the event to see if any event handlers have been attached. If so, it must iterate the entire invocation list. Each method in the event invocation list must be called. Determining whether there are event handlers and iterating each at runtime takes more execution time than invoking one virtual function.

If that's not enough for you, examine the first listing in this item again. Which is clearer? Overriding a virtual function has one function to examine and modify if you need to maintain the form. The event mechanism has two points to maintain: the event handler function and the code that wires up the event. Either of these could be the point of failure. One function is simpler.

Okay, I've been giving all these reasons to use the overrides and not use the event handlers. The .NET Framework designers must have added events for a reason, right? Of course they did. Like the rest of us, they're too busy to write code nobody uses. The overrides are for derived classes. Every other class must use the event mechanism. For example, you often add a button click handler in a form. The event is generated by the button, but the form object handles the event. You could define a custom button and override the click handler in that class, but that's way too much work to handle one event. It only moves the problem to your own class anyway: Somehow, your custom button must communicate to the form that the button was clicked. The obvious way to handle that is to create an event. So, in the end, you have created a new class to send an event to the form class. It would be simpler to just attach the form's event handler to the form in the first place. That's why the .NET Framework designers put those events in the forms in the first place.

Another reason for the event mechanism is that events are wired up at runtime. You have more flexibility using events. You can wire up different event handlers, depending on the circumstances of the program. Suppose that you write a drawing program. Depending on the state of the program, a mouse down might start drawing a line, or it might select an object. When the user switches modes, you can switch event handlers. Different classes, with different event handlers, handle the event depending on the state of the application.

Finally, with events, you can hook up multiple event handlers to the same event. Imagine the same drawing program again. You might have multiple event handlers hooked up on the MouseDown event. The first would perform the particular action. The second might update the status bar or update the accessibility of different commands. Multiple actions can take place in response to the same event.

When you have one function that handles one event in a derived class, the override is the better approach. It is easier to maintain, more likely to be correct over time, and more efficient. Reserve the event handlers for other uses. Prefer overriding the base class implementation to attaching an event handler.
 
   

   

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.