C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2008/09/28 02:50

static class Program
{
    static void Main()
    {
        Hoge h = null;
        h.Safely().Fuga();
    }
}

public class Hoge
{
    static public readonly Hoge Null = new NullHoge();

    public virtual void Fuga()
    {
    }

    private class NullHoge : Hoge
    {
    }
}

static public class HogeExtension
{
    static public Hoge Safely(this Hoge source)
    {
        return (source != null) ? source : Hoge.Null;
    }
}


Imports System.Runtime.CompilerServices

Module Module1
    Sub Main()
        Dim h As Hoge = Nothing
        h.Safely()
        h.Fuga()
    End Sub
End Module

Public Class Hoge
    Public Shared ReadOnly Null As Hoge = New NullHoge()

    Public Sub Fuga()
    End Sub

    Private Class NullHoge
        Inherits Hoge
    End Class
End Class

Public Module HogeExtension
    <Extension()> _
    Public Sub Safely(ByRef source As Hoge)
        If (source Is Nothing) Then
            source = Hoge.Null
        End If
    End Sub
End Module
スポンサーサイト



タグ: .NET C# VB
2007/10/24 22:50

Visual Basic でなんとなくイベントとデリゲート その3。 - イベントに関連することをちょっとだけ追記。 -

とりこびとさんの記事を読んでいて、そういえば VB のイベント定義って C# と違うんだよなぁと思い出したことがきっかけで、久々に VB のコードを書いてみました。そしたら、ちょっとした問題に気づきました。


とりあえず、C# でのイベント定義から。
C# でイベントを定義する場合、通常は以下のように記述します。

public class Class1
{
    private int _field1;

    public int Property1
    {
        get
        {
            return this._field1;
        }
        set
        {
            if ((this._field1 != value) && (this.Event1 != null))
            {
                this.Event1(this, EventArgs.Empty);
            }
            this._field1 = value;
        }
    }

    public event EventHandler Event1;
}


この書き方は、実はイベント定義のシンタックスシュガーであり、以下のように add アクセサと remove アクセサを自分で実装することも可能です。

public class Class1
{
    private int _field1;

    public int Property1
    {
        get
        {
            return this._field1;
        }
        set
        {
            if ((this._field1 != value) && (this._event1 != null))
            {
                this._event1(this, EventArgs.Empty);
            }
            this._field1 = value;
        }
    }

    private EventHandler _event1;

    public event Eventhandler Event1
    {
        add
        {
            this._event1 += value;
        }
        remove
        {
            this._event1 -= value;
        }
    }
}



Visual Basic の場合、以下のように書くようです。

Public Class Class1

    Private _field1 As Integer

    Public Property Property1() As Integer
        Get
            Return Me._field1
        End Get
        Set(ByVal value As Integer)
            If (Not Me._field1 = value) Then
                RaiseEvent Event1(Me, EventArgs.Empty)
            End If
            Me._field1 = value
        End Set
    End Property

    Private _event1 As EventHandler

    Public Custom Event Event1 As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me._event1 = CType(System.Delegate.Combine(Me._event1, value), EventHandler)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me._event1 = CType(System.Delegate.Remove(Me._event1, value), EventHandler)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
            If (Not Me._event1 Is Nothing) Then
                Me._event1(sender, e)
            End If
        End RaiseEvent
    End Event

End Class


C# では add アクセサと remove アクセサの2つを定義すれば良かったのですが、VB の場合、AddHandler アクセサと RemoveHandler アクセサの他に、RaiseEvent アクセサというものも定義しなければなりません。
アクセサを自前で実装せずにイベントを定義した場合は、コンパイルされた MSIL に RaiseEvent アクセサに相当するコード ( MSIL では、RaiseEvent アクセサは fire アクセサとなる模様 ) は出力されません。しかし、自前で実装する場合はなぜか必須です。

なぜ必須とするのか、考えてみました。
VB ではイベントを発生させるのに RaiseEvent ステートメントを使用します。もし、自前で実装したイベントに RaiseEvent アクセサがなかった場合、RaiseEvent ステートメントでのイベント発生ができなくなります。代わりに C# のようにデリゲートの Invoke メソッドを呼び出せばいいだけの話なのですが、それを嫌ったのかなぁと。


で、これ実は結構問題なんじゃないかと思うんです。イベントのアクセサを自前で実装するのって、他のオブジェクトのイベントへ委譲する場合が主だと思うんです。 ( 僕はこれ以外の理由でわざわざ自前実装したことがありません。 )
例えば、ユーザーコントロールを開発するとします。このユーザーコントロールには Enter ボタンを配置します。
ユーザーコントロールの外部からは Enter ボタンにアクセスできないようにしたい、でも Enter ボタンのクリックイベントは外部に公開したい。そんな時、ユーザーコントロールに EnterClick イベントを定義してイベントのアクセサを自前で実装するわけです。
AddHandler アクセサは Enter ボタンのクリックイベントにハンドラを登録します。
RemoveHandler アクセサは Enter ボタンのクリックイベントからハンドラを削除します。
では、RaiseEvent アクセサは?
ボタンのクリックイベントの公開が目的なのだから、ユーザーコントロール内のコードでイベント発生なんてさせませんし、できません。でも RaiseEvent アクセサを定義しないといけません。恐らく、何の処理もしない、あるいは例外を発生させるという実装になるかと思います。そして、ユーザーコントロール内のコードに

RaiseEvent EnterClick(Me, EventArgs.Empty)

って書くことは許されてしまうわけです。これをコンパイル時に検出することはできません。


ちなみに、RaiseEvent アクセサに相当するものは C# にはありません。
個人的には、あってもなくてもどっちでもいいって感じですが、これを必ず定義しなきゃいけないってのはよろしくないよなぁと思いました。


[ 参考 ]
Event ステートメント ( Visual Basic )
タグ: .NET C# VB