Blank?=False

ゆるゆる仕事したいフリーランスエンジニアの記事

VBのWithを使うと可読性が下がる気がする。

VBVBAにはWithという構文があります。
このWithを使うとソースコードの可読性が下がるような気がするんです。

Withとはどのような構文か

指定したオブジェクトをステートメント内だけオブジェクトの宣言の省略が可能になる。

たとえば、FooというオブジェクトをWithに指定した場合、

With Foo
  .Attr = 1
  .Run
End With

と言った風

どのようなときに使うか

例えば、こういう風に1つのオブジェクトに対して複数の操作を行うとします。

Dim Foo As FooObject
Foo = New FooObject

Foo.Attr = 1
Foo.State = "Run"
Foo.Mode = FOO_MODE_NORMAL

Foo.Run


Foo.が重複しているのでちょっと冗長に感じしまうかもしれません。
そこで、Withを使うと下のように束ねることができます。

Dim Foo As FooObject
Foo = New FooObject

With Foo
   .Attr = 1
   .State = "Run"
   .Mode = FOO_MODE_NORMAL
   .Run
end With

こういったふうに、Withで指定したオブジェクトの宣言を省略する事ができます。
こうすることで、同一のオブジェクトに対する複数の操作を簡略化して読みやすくすることができる、と謳われています。

なぜ可読性が下がるのか

これ、自然言語での主語をオブジェクト、動詞をメソッドと置き換えて考えてみると、
主語を省略して動詞だけ言ってるような状態なんですね。

例えば、「本を読んだ」を「読んだ」としか言わない状態です。
文脈として考えるなら、それより前に「本」の話が出ているはずです。
なので、「本」をWithに指定した、と考えれば「読んだ」がメソッドとして考える事ができます。

コレだけ聞くと、なんでそれで可読性が下がるの?とお思いでしょう。

例えば、このように幾つものオブジェクトに対し操作する関数があったとします。

Sub Foo( Bar As BarObject ) 
  With Bar
    Dim Baz as BazObject
    Baz = new BazObject
    Dim Zoo As ZooAbject
    Zoo = new ZooObject

    Baz.Attr = 1
    Zoo.Attr = 1
    .Attr = 1

    Bar.Str = "1"
    Zoo.Str = "1"
    .Str = "1"
  end With
End Sub

どうですか?
主語がないメソッドはちょっと読みにくく感じませんか?
それそのはず、Withで指定したオブジェクトはコードを読む時にどのオブジェクトがWith指定されているか、頭の片隅に置いて置く必要があります。
また、他のオブジェクトは主語があるのにWith指定したオブジェクトは主語がないため、またコードに一貫性がなく、読みづらい印象を受けます。

こういった観点から、Withを使うと読みにくく感じてしまうわけです。

ネストができてしまう

おまけですが、このWithもネストができてしまいます。
このネストがますます可読性を下げてしまうんじゃないかなと考えています。

 Class FooObject
        Public Sub Run()
            Console.WriteLine("Foo.Object")
        End Sub
    End Class

    Class BarObject
        Public Sub Run()
            Console.WriteLine("Bar.Object")
        End Sub
    End Class


    Sub Main()
        Dim Foo As FooObject
        Foo = New FooObject

        With Foo
            Dim Bar As BarObject
            Bar = New BarObject

            With Bar
                .Run()
            End With

            .Run()

            Console.ReadLine()
        End With
    End Sub


実行結果

Bar.Object
Foo.Object

これでWithステートメントが長い行だった場合、ますます理解を困難にさせてしまうと思います。

他の言語にはない

このWithステートメントですが、対応している言語は他に知りません。
少なくとも、C#C/C++Rubyでは見たことがありません。

これは、他の言語をやっている人にとっては全くの未知の制御構文で、
他の言語をメインにやってる人が緊急時にVBを触った場合、リファレンスを読む必要が出てしまいます。

こういった観点から、驚き最小の法則に従っておらず、読みにくく感じてしまう人もいるかもしれません。

改善策

  • Microsoftのリファレンスの例もある通り、Withステートメント内には1つのオブジェクトしか操作させないようにします。
Private Sub AddCustomer()
    Dim theCustomer As New Customer

    With theCustomer
        .Name = "Coho Vineyard"
        .URL = "http://www.cohovineyard.com/"
        .City = "Redmond"
    End With

    With theCustomer.Comments
        .Add("First comment.")
        .Add("Second comment.")
    End With
End Sub

Public Class Customer
    Public Property Name As String
    Public Property City As String
    Public Property URL As String

    Public Property Comments As New List(Of String)
End Class

コーディングルールとして策定するなら

もし、Withの使い方についてコーディングルールを作るなら、以下のようにします。

  • Withのステートメント内はWithに指定したのオブジェクトのみを使用する
  • Withのネストは行わない

参考文献・サイト

With...End With Statement (Visual Basic)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)