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
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 )