VB.NETはヤバい

最近VB.NETと戯れてるんですが、C#と同じフレームワーク上なのにスゲー書きづらいことが分かりました。

煮え切らない感じが続いてるのでこのあたりでVB.NETのヤバいとこを書いてストレス解消しておきます。

たとえば

こんなクラスがあったとしましょう。

Public Class TestClass

    Public Delegate Sub SomeEventHandler(result As String)
    Public Event SomeEvent As SomeEventHandler

    Private _target As String
    Public Property Target() As String
        Get
            Return _target
        End Get
        Set(value As String)
            _target = value
        End Set
    End Property

    Public Sub New()

    End Sub

    Public Sub CallEvent()
        RaiseEvent SomeEvent(Me._target)
    End Sub


End Class

クラスオブジェクト内のフィールドの値をイベントハンドラ経由で返してくれるイベントを持ったクラスです。

こいつを使う時にラムダ式でカジュアルにコールバックメソッドを追加してやります。

Module Module1

    Sub Main()
        Dim test As TestClass = New TestClass()
        Dim str As String = "Test data"
        test.Target = str

        Dim callback As String = "Empty data"

        'これは正常に動かない
        AddHandler test.SomeEvent, Function(x) callback = x
        test.CallEvent()

        Console.WriteLine(callback)
        '#>Empty data


    End Sub

    Private Sub MyEventHandler(sender As String)
        _callback = sender
    End Sub


End Module

こうすればローカル変数callbackにTestClassのフィールドの値"Test data"がそのまま入ってくるような感じがしますが、 実際にはうまくいかず、"Empty data"が表示されてしまいます。

もしコールバックメソッドで値を代入するには、次のように記述する必要があります。

Module Module1

    'プライベートフィールドを定義
    Private _callback As String = "Empty Data"

    Sub Main()
        Dim test As TestClass = New TestClass()
        Dim str As String = "Test data"
        test.Target = str

        'これはOK
        AddHandler test.SomeEvent, AddressOf MyEventHandler
        test.CallEvent()

        Console.WriteLine(_callback)
        '#>Test data

    End Sub

    'コールバック関数をちゃんと定義
    Private Sub MyEventHandler(sender As String)
        _callback = sender
    End Sub


End Module

C#だとこういう問題は起きません。困ったさんですね。 同じフレームワークならこの辺りの挙動も同じにしてほしいんですが・・・。

ちなみにC#で書くとこんな感じになります。いいっすね。

    class Program
    {
        static void Main(string[] args)
        {
            var test = new TestClass();
            test.Target = "Test data";
            var callback = "Empty data";
            test.SomeEvent += (x) => callback = x;
            test.CallEvent();
            Console.WriteLine(callback);
            //#>Test data
        }
    }

    class TestClass
    {
        private string _target = "";

        public delegate void SomeEventHandler(string result);
        public event SomeEventHandler SomeEvent;

        public string Target
        {
            get
            {
                return _target;
            }
            set
            {
                _target = value;  
            }
        }

        public TestClass()
        {

        }


        public void CallEvent()
        {
            SomeEvent(_target);
        }
    }

ここがヤバい!VB.NET

ラムダ式の構文が冗長

C#ではアロー演算子でサクっと記述できていたので結構ラムダ式好きだったんですが、VB.NETはかなり冗長だなーと思います。

たとえばEnumerableなクラスオブジェクトに対して.FirstOrDefaultメソッドを呼ぶときは

var result =Target.FirstOrDefault(x=> x.value == anyObj.Text).Key

みたいにいい感じがするんですが、VB.NETでは

Dim result as String = Tareget.FirstOrDefault(function(x) x.value = anyObj.Text).Key

はい・・・。

まずFunction(x)ってなんなの。めっちゃ「はーい!僕が匿名メソッドでーす!」みたいな感じ丸出しでダサい。(完全に個人的な感想)

あとアロー演算子が不要なのが、かえって可読性を下げていると思います。

そんでもって挙動がC#と違う?っぽいのが結構キてます。渡し方によってはラムダ式でも同じ挙動にできるのか正直わかってないですけど。

あとDimって書かせてから変数名書いた後に「as ClassName」ってのも冗長で好きじゃないですね。

イベントハンドラの構文が分かりづらい

AddHandler testClass.SomeEvent, AddressOf MyEventHandler 

とか、

RaiseEvent SomeEvent(_target)

とか。

VB.NETが生まれた背景を考えると仕方ないんでしょうけど、さすがにAddressOf演算子を使わないとイベントハンドラ登録できないって結構ヒドいなと思います。

シンタックスシュガーぐらい用意してもいいんじゃないんでしょうかね。

そもそも.NET Frameworkってアンセーフコードでない限りポインタ演算子をサポートしないはずでは?構文にポインタの概念が漏れ出しまくってるけど大丈夫?

おわりに

ストレスが解消されたのでよかったです。でもVB.NETはしばらく触りたくないですね。