イベントハンドラの修飾子はprivateか?protectedか?

前々から気になっていたんだけど、業務中にたまたま発見しました。

VBではどうなのか

ASP.NET Web Formの開発でコードビハインドをVBで書いている方は、
Visual Studioのエディタの上の方にあるドロップダウンから選択して、
イベントを追加している方もいらっしゃるのではないでしょうか?

その時に追加されるイベントメソッドの修飾子はprivateになっています。
(少なくともVS2010と2012はそうなるはずです。)

しかし、aspxファイルを追加したときに既に追加されているPageのLoadイベントは、
protectedになっているんですね。

前にも気になって全部のイベントメソッドの修飾子をprivateにしたりprotectedにしたり、
色々と弄ってみたんですが両方とも問題なく動きました。

C#ではどうなのか

C#だとVBのようなイベントの追加は出来ず、イベントを追加しようと思ったら、
コードビハインド側に適切なEventArgsを引数にしたメソッドを追加して、
それをaspx側でコントロールのOnXxx(On + イベント名)プロパティで指定してやらなければいけません。
(Pageのイベントは例外ですが、これについては後述します。)

ただ、この時aspx側で指定するメソッドはprotectedでなくprivateにすると、
実行時にエラーになります。

なぜVBはprivateで良くてC#ではダメなのか

コートビハインドをVBで書いたときとC#で書いたときの違いに、
イベントの紐付け方の違いがあります。

VBではHandles句というものがあり、イベントのメソッド名の後にHandles句を書くことで、
コードビハインド側だけでコントロールとイベントの紐付けを行うことができます。

Private Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Button.Click
    ' イベント処理
End Sub

しかしC#にはHandles句がないため先に述べたような方法でイベントを紐付ける必要があります。

・aspx.cs側

protected void Button_Click(object sender, EventArgs e)
{
    // イベント処理
}

・aspx側

<asp:Button ID="Button" runat="server" OnClick="Button_Click" />

で、なぜこのときにprivateではいけないのかというと、
aspxはコードビハインド側のクラスを継承しています。

aspxファイルの一番最初に書いてありますね。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm.aspx.cs" Inherits="WebApp.WebForm" %>

上記ではaspxがWebFormというコードビハインドが書かれたクラスの子クラスにあたるため、
コードビハインドに書かれたメソッドを参照するには修飾子がprotected以上でないとダメなんですね。

VBでもC#と同様にHandles句を使わずにaspxでイベントの紐付けが出来ますが、
この場合はやはり修飾子はprotectedでなければなりません。

C#でのPageイベント

C#でPageのイベントのバインドを自動的に行うかどうかは、
aspxのAutoEventWireupプロパティに依存します。
(ひとつ上のソースを参照して下さい。)

このプロパティがtrueの場合は勝手にイベントを紐付けてくれますが、
falseにした時は下記のようにコードビハインドクラスのコンストラクターで、
明示的に紐付けなければなりません。

public partial class WebForm : System.Web.UI.Page
{

    /// <summary>
    /// コンストラクター
    /// </summary>
    public WebForm()
    {
        this.Init += new EventHandler(this.Page_Init);
        this.Load += new EventHandler(this.Page_Load);
    }

    /// <summary>
    /// Page_Initイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Init(object sender, EventArgs e)
    {
    }

    /// <summary>
    /// Page_Loadイベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
    }

}

この時はaspx側がコードビハインド側のメソッドを参照することはないので、
イベントの修飾子はprivateでも大丈夫です。

ちなみにVBの場合はPageのイベントもHandles句で紐付けてくれてますね。

C#でもコードビハインドだけでイベント紐付け

実はC#でもコードビハインドでイベントの紐付けが出来ます。

この場合は以下のようにPageのInitイベントでイベントを付与します。

/// <summary>
/// Page_Initイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Init(object sender, EventArgs e)
{
    this.Button.Click += new EventHandler(this.Button_Click);
    this.DropDownList.SelectedIndexChanged += new EventHandler(this.DropDownList_SelectedIndexChanged);
}

/// <summary>
/// Button_Clickイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Button_Click(object sender, EventArgs e)
{
}

/// <summary>
/// DropDownList_SelectedIndexChangedイベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void DropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
}

この場合もイベントメソッドの修飾子はprivateでも大丈夫です。

結論

VBの場合

イベントの紐付けを…
・Handles句を使用するならprivateでOK
・aspxで紐付けるならprotectedでないとNG

C#の場合

イベントの紐付けを…
・Page_Initイベントで行うならprivateでOK
・aspxで紐付けるならprotectedでないとNG

こんなカンジですかね。

何で今まで気付かなかったのか不思議なんですが、
一つ疑問が解消できてスッキリしました。

同じ疑問持っている人っているのかな…。

カテゴリー: ASP.NET, C#, VB, Web Forms パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>