2011年12月5日月曜日

MethodInfo からのインテリセンス用ヒント文字列の生成

現在 α 版としてですが、インテリセンスにある程度対応した状態にて WF Designer Express をアップさせてもらっています。インテリセンス自体もまだまだ改良中ですが、その際に表示するツールチップヒントの文字列生成というのが、なかなかよく分かっていなかったのでまとめてみました。

メソッドの情報自体は MethodInfo クラスにて参照できるようになっています。MethodInfo クラス自体は Type.GetMethods などで抽出する事が可能です。そして抽出した MethodInfo クラスのプロパティを色々見る事で、ヒント用の文字列(Public Sub New とか、Private Function Test(ByVal a As String) As String とか)を生成する事が可能です。

   1: Private Function CreateMethodDescription(ByVal target As MethodInfo) As String
   2:     Dim desc As New Text.StringBuilder
   3:     If target.IsPublic Then desc.Append("Public ")
   4:     If target.IsFamily Then desc.Append("Protected ")
   5:     If target.IsAssembly Then desc.Append("Friend ")
   6:     If target.IsPrivate Then desc.Append("Private ")
   7:     If target.IsAbstract Then desc.Append("MustOverride ")
   8:     If target.IsVirtual AndAlso Not target.IsFinal Then desc.Append("Overridable ")
   9:     If target.IsStatic Then desc.Append("Shared ")
  10:  
  11:     If (Not target.ReturnType Is GetType(Void)) Then
  12:         desc.Append("Function ")
  13:     Else
  14:         desc.Append("Sub ")
  15:     End If
  16:  
  17:     desc.Append(target.Name)
  18:     desc.Append(CreateGenericParameter(target))
  19:  
  20:     desc.Append("(")
  21:     Dim paramIndex As Integer = 0
  22:     For Each param In target.GetParameters
  23:         If paramIndex > 0 Then desc.Append(", ")
  24:         If param.IsOptional Then desc.Append("Optional ")
  25:         If param.IsOut Then
  26:             desc.Append("ByRef ")
  27:         Else
  28:             desc.Append("ByVal ")
  29:         End If
  30:         desc.Append(param.Name + " As " + param.ParameterType.Name)
  31:         desc.Append(CreateGenericParameter(param.ParameterType))
  32:         If Not IsDBNull(param.DefaultValue) Then
  33:             If param.DefaultValue Is Nothing Then
  34:                 desc.Append(" = Nothing")
  35:             Else
  36:                 desc.Append(" = " + param.DefaultValue.ToString)
  37:             End If
  38:         End If
  39:         paramIndex += 1
  40:     Next
  41:     desc.Append(") ")
  42:     If target.ReturnType IsNot Nothing Then
  43:         desc.Append("As " + target.ReturnType.Name)
  44:         desc.Append(CreateGenericParameter(target.ReturnType))
  45:     End If
  46:     Return desc.ToString
  47: End Function
  48:  
  49: Private Overloads Function CreateGenericParameter(ByVal target As MethodInfo) As String
  50:     Dim result As New Text.StringBuilder
  51:     If target.IsGenericMethod Then
  52:         result.Append("(Of ")
  53:         Dim genIndex As Integer = 0
  54:         For Each genParam In target.GetGenericArguments
  55:             If genIndex > 0 Then result.Append(", ")
  56:             result.Append(genParam.Name)
  57:             genIndex += 1
  58:         Next
  59:         result.Append(")")
  60:     End If
  61:     Return result.ToString
  62: End Function

一気にロジックを掲載しますが、基本力技でやりましたw

まずメソッドが Public なのか Private なのかについてを前半部分にて行っています。ここでは Public、Protected、Friend、Private、MustInherits、Overridable、Shared については判別できますが、Overloads については判別できません。力技でやるとすると該当するクラスのメソッド情報をみて同名のメソッドがあるかどうか、で行うのだと思います。

続いて行っているのは、Sub か Function かの判定です。VB 使いとしては短絡的に
「値を返さない=ReturnType が Nothing」
のようにすぐ思ってしまったのですが、値を返さない=Void ですよね……。

ジェネリックについては後述するとして、渡される引数についての判定として GetParameters メソッドで取得した引数情報を元にループ処理を行います。ここでは VB 使いならではの点があり(最近の C# では記述できるけど)Optional と ByRef、そして Default の判断も行っています。少しひっかかったのは Default でして、初期値=Nothing、という記述は普通にできる事をすっかり失念していました。なお初期値がない場合は、DefaultValue プロパティに DBNull が設定されるという、少々特殊な状況になりますので気を付けてください。

後回しにしたジェネリックですが、別メソッドに切り分けてある部分が該当します。今回は MethodInfo からの処理について記載していますが、実際にはもう一つ Type からの処理版も作成しています。違いはジェネリックかどうかを判断する部分の聞き方だけで、MethodInfo の場合は IsGenericMethod プロパティにて、Type から行う場合は IsGenericType プロパティにて判断する必要があります。判断後は全く一緒です。

このような処理を行う事で、インテリセンスで利用するヒント文字列を作成する事ができます。

0 件のコメント:

コメントを投稿