しばたテックブログ

気分で書いている技術ブログです。

拡張メソッドのちょっと変わった使い方

VB.NET向けのはなしです。
というかVB.NETにObsolete属性による警告を抑制するpragma warning disable 618が無いための苦肉の策でもあります。*1

VB14(Visual Studio 2015)以降の場合

【2017/02/09追記】

VB14(Visual Studio 2015)でようやくVB.NETにもpragma warning disableと同等の文法が提供されました。

#Disable Warning [警告のコード]で以降の警告を非表示にし、#Enable Warning [警告のコード]で警告を表示する様に戻すことができます。

実行例)

#Disable Warning BC40000
Dim Result = (New SampleClass).OldFunc()
#Enable Warning BC40000

ようやくまともになってくれた感がありますね。

【追記ここまで】

拡張メソッドのちょっと変わった使い方

例として適当なクラスSampleClassと何らかのデータを取得するメソッドOldFuncを挙げてみます。 ソースはこんな感じ。

SampleClass.vb

''' <summary>
''' サンプルのクラスです。
''' </summary>
Public Class SampleClass

    ''' <summary>
    ''' なんらかのデータをDataTableの形で取得するメソッド。
    ''' </summary>
    Public Function OldFunc() As DataTable
        'データを取得する処理を適当に...
    End Function

End Class

で、このOldFuncDataTableを返すのが非常に前時代的なので、新しいメソッドNewFuncを作ってこちらの使用を推奨したとします。
ただ、OldFuncを完全に廃止することが出来ずにどうしても使い続けなければならないケースがある場合、一般的にはObsolete属性をつけて非推奨である旨を明示することになると思います。

そうするとNewFuncを追加した後のソースはこんな感じになるはずです。

SampleClass.vb

''' <summary>
''' サンプルのクラスです。
''' </summary>
Public Class SampleClass

    ''' <summary>
    ''' なんらかのデータをDataTableの形で取得するメソッド。
    ''' ※このメソッドはもう非推奨!
    ''' </summary>
    <Obsolete("NewFuncを使用してください。")>
    Public Function OldFunc() As DataTable
        'データを取得する処理を適当に...
    End Function

    ''' <summary>
    ''' 新しくデータを取得するメソッド。推奨メソッド!
    ''' </summary>
    Public Function NewFunc() As IEnumerable(Of IRecord)
        'データを取得する処理を適当に...
    End Function

End Class

Obsolete属性が付いたのでSampleClass.OldFuncメソッドを呼び出そうとすると以下の様に警告が出る様になります。

f:id:stknohg:20150806185828p:plain

ここでC#なら以下の様にpragma warning disable 618を設定することでOldFuncを使わざるを得ない特定の場合だけ警告を抑制することができます。

#pragma warning disable 618
var Result = SampleClass.OldFunc();
#pragma warning restore 618

しかしながら、本当に本当に残念なことにVB.NETだとそれができません。

そこで、以下の様にNewFuncを追加するのと同時にOldFuncを別名前空間のモジュールへ拡張メソッドの形で移動してやります。

SampleClass.vb

''' <summary>
''' サンプルのクラスです。
''' </summary>
Public Class SampleClass

    '
    ' OldFunc()はSampleClassObsolete.vbへ移動する
    '
   
    ''' <summary>
    ''' 新しくデータを取得するメソッド。
    ''' </summary>
    Public Function NewFunc() As IEnumerable(Of IRecord)
        'データを取得する処理を適当に...
    End Function

End Class

SampleClassObsolete.vb

Imports System.Runtime.CompilerServices
' 名前空間を別にする
Namespace Obsolete

    ''' <summary>
    ''' 非推奨のメソッドを別名前空間の拡張メソッドに移動してしまう。
    ''' </summary>
    Module SampleClassObsolete

        ''' <summary>
        ''' データを取得するメソッド。
        ''' ※このメソッドはもう非推奨!
        ''' </summary>
        <Extension>
        Public Function OldFunc(this As SampleClass) As DataTable
            'データを取得する処理を適当に...
        End Function

    End Module

End Namespace

こうすることによって名前空間を明示した時だけOldFuncが使用可能になり、非推奨の警告が残り続けることが無くなります。

使用法

以下の様な感じで使います。

通常時

OldFuncは別モジュールに移したので通常時は使用することができなくなります。

App.vb

Module App

    Public Sub Main(args As String())

        ' ※この呼び出しはエラーとなる
        Dim Result = New SampleClass().OldFunc()

    End Sub

End Module

当然インテリセンスにもOldFuncは表示されません。

f:id:stknohg:20150806190012p:plain

OldFuncを使う場合

OldFuncを使いたい時だけ名前空間を明示してImportしてやります。

App.vb

Imports SampleApp.Obsolete

Module App

    Public Sub Main(args As String())

        ' Imports SampleApp.Obsolete をすればOldFunc()を呼び出せる。
        Dim Result = New SampleClass().OldFunc()

    End Sub

End Module

するとSampleClass.OldFuncを使用することができる様になります。
インテリセンスにもOldFuncが表示されます。

f:id:stknohg:20150806190101p:plain

所感

苦肉の策ではありますが意外と使えています。
非推奨の処理を外に切り出せる点と、明示的にImportしない限り非推奨のメソッドが見えない・使えない点が意外と良い感じに機能しました。

あまり褒められた方法ではないとは思いますが、こんな方法もあるよという事で。

*1:一応VB.NETでもプロジェクト単位であれば警告の抑制ができるのですがプロジェクト単位なんて粒度が荒すぎて使い物にならないです...