ページ

2014年12月8日月曜日

[.NET] VB って If a = a Then が成り立たないことがあるのか!

先日、VB のコードを書いていて初めて知って衝撃を受けました。

Dim i As Integer? = Nothing
If i <> 1 Then
    i = 1
End If

なんのためらいもなくこういうコードを書いてました。当然 i は 1 ではないので If 文の中に入って 1 が代入されるもんだと思ってました。もちろん C# ではそうなります。けど、VB は違うんですね。コンパイラがどういうことやってるのかとりあえず IL を見てみました。IL の内容をそのまま VB で書くと

Dim result As Boolean?
If Not i.HasValue Then
    result = False
Else
    Dim compare As Boolean? = Not i.Value = 1
    result = compare
End If
If result Then
    i = 1
End If

こんな感じでした。(最適化無しのデバッグビルドです。最適化すればもっと効率いい IL になるんじゃないかと思います)
まず i が Nothing でないかを HasValue でチェックして、Nothing でない場合だけ 1 と比較しています。判定結果をいったん Boolean?(Nullable<bool>)に代入してる理由はよくわかりません。普通に Boolean で事足りると思うんですがなんでわざわざ Boolean? なんでしょうね?

というわけで、VB では比較する二項のいずれかの型が Nullable であり、その値が Nothing のときは常に条件は成り立ちません。たとえ等値演算子の両項が Nothing であっても成り立ちません。実際に以下のコードを試してみると「i0 と i1 は違う」と表示されます。

Dim i0 As Integer? = Nothing
Dim i1 As Integer? = Nothing
If i0 = i1 Then
    Console.WriteLine("i0 と i1 は同じ")
Else
    Console.WriteLine("i0 と i1 は違う")
End If

さらに同じ変数どうしでもダメです。

Dim a As Integer? = Nothing
If a = a Then
    Console.WriteLine("a と a は同じ")
Else
    Console.WriteLine("a と a は違う")
End If

これでも「a と a は違う」と表示されます。

もちろん、このことはリファレンスに載っています。
null 許容値型 (Visual Basic)
ここの「Null 許容型の比較」にあるように Nullable を比較した結果は True、False、Nothing のいずれかになり、そして Nothing は True でも False でもありません。さらに、Nothing は = や <> で比較することはできません(結果は常に Nothing になるので)。Nothing を比較できるのは Is か IsNot 演算子だけです。

いやぁ、びっくりした。
参照型の場合は、If obj Is Nothing Then のように Is、IsNot 演算子を使う癖がついてます。Nullable は値型だけど「なるべく参照型っぽく振る舞うようになっている」と思えば Is、IsNot を使わないとダメっていう発想になるかもしれませんが、なんとなく「値型だから」という気がしてなんの疑問も持たずに =、<> を使っちゃってました。そうか、ダメなのか。まぁ、気づいてしまえば確かに参照型っぽくしようと思うとこういう仕様になるっていうのは納得できますが。けど、上の例のように同じ変数の比較でも Nothing だと成り立たないっていうのはちょっとどうなのかなぁ。
それにしても、今までたまたま VB で Nullable を使うコードを書くことがほとんど無かったのもあって、ほんとにまったく気づいてませんでした(C# では Nullable 使いまくってますが)。

ちなみに、このことに気づいたのは INotifyPropertyChanged を実装していた時です。C# でオーソドックスに書いた時と同じように

Public Property MyValue As Integer?
    Get
        Return Me._MyValue
    End Get
    Set(value As Integer?)
        If Me._MyValue &lt;&gt; value Then
            Me._MyValue = value
            RaisePropertyChanged("MyValue")
        End If
    End Set
End Property

こんな風に書いて意図したように動かなくて気づいたわけです。(Me._MyValue か value が Nothing だとうまく動かない)
ではどういう風に書けばいいんだろう?と考えてみましたが、どうやら、

Public Property MyValue As Integer?
    Get
        Return Me._MyValue
    End Get
    Set(value As Integer?)
        If Not Me._MyValue.Equals(value) Then
            Me._MyValue = value
            RaisePropertyChanged("MyValue")
        End If
    End Set
End Property

と書けばいいようです。
Nullable の場合、一見 null になっていても HasValue、GetValueOrDefault() といったプロパティ/メソッドは呼び出せます。HasValue 呼び出せなかったら値が null かどうかチェックできなくなってしまうのでそりゃそうですよね。そもそも、あくまで Nullable<T> の中身が null であることを表しているだけであって Nullable<T> 自体のインスタンスはあるわけですから呼び出せて当然です。そして同様に Equals() も呼び出せます。Equals() は C# での == と同じ結果を返してくれるのでこれでうまくいきます。
もちろん、参照型の場合に値が  null なのに Equals() とか呼び出すと NullReferenceException なので注意。

と、今さらのように知って衝撃を受けたので長々と書いてみました。(VB にはまだまだ気づいてないことがありそう)

2014年12月1日月曜日

[勉強会] Room metro #28 大阪に参加してきた

2014/11/29 に開催された Room metro #28 大阪 に参加しました。
「XAML Day」ということで、XAML をテーマにした勉強会。

私も「XAML 入門」のセッションを担当しました。XAML の特徴や機能を Visual Studio なんかでデモりながらざっくり紹介してみました。セッション資料は slideshare にあげてありますが、デモ中心のセッションにしたのでセッション資料は見出ししかない感じですが。


他のみなさんのセッションはどれもとてもおもしろかったですし、勉強になることもいろいろありました。ほんと、XAML がテーマと言っても Blend あり、WF(Windows Workflow Foundation)あり、おもしろいデモありといろんなジャンルの話が聞けました。
そして、最後は東京から来ていただいたぐらばくさんのセッション。さすが艦これビューワーの作者さんということで、ガリガリと XAML を書いていく様は圧巻(きっと頭のなかに XAML パーサーが搭載されているんだろうという話に納得)。とりあえず、今風なアプリにするにはウインドウの枠を光らせておけばいいようです(違

セッション終了後は場所はそのままで希望者でハッピーアワー。軽食や飲み物を用意して立食形式でワイワイガヤガヤ。とても楽しかったです。そういえば、せっかくプロジェクターとかあったんだから何か映しといたらよかったかも。ただ、主催のさおさんもこういう形式は初めてということで食料が無くなってしまいそうになり途中で追加の買い出しに行ったりとちょっとバタバタしたところもありました。お店でやる懇親会とは違う良さはありますが、やっぱりこういう形式だと運営側に負担がかかっちゃいますね。コツを掴めば適度な量を用意できるようになるのかな?

さらにその後はお茶を飲みながら(私はビール飲んでましたが)二次会。後半は某社のネタで盛り上がってました(笑)

ぐらばくさんを中心に何人かの方は翌日京都観光に行くとのこと。私はシンデレラたちをプロデュースするために代々木競技場に行かなくてはいけないため残念ながら参加できず。