Windows Live Writer SDK関連のための話しです。WLWのプラグイン作成時に IProperties というインタフェースが出てきます。これは次のようにプラグイン固有のまたはプラグインにより挿入したコンテンツ固有の情報を取得・設定するために用意されています。
Dim foo = Options.GetString("Foo", "foo")
Options.SetString("Foo", "bar")
OptionsがIProperties型。情報を取得する場合、GetString(名前, デフォルト値)と呼び出します。設定する場合は、SetString(名前, 設定値)となります。基本的な型についてはそれぞれGetIntなどのメソッドが用意されてます。
このIPropertiesを上のようにそのまま使用すると大量に文字列リテラルが出てきます。リテラルじゃないのは設定値となる"bar"のところだけ。単純に解決するには全部定数に置き換えます。
Const Foo As String = "Foo"
Const FooDefaultValue As String = "foo"
するとこうなる。単純で良いのかもしれないけれど、Fooという名前にFooと値をいれてる、この例ではFooひとつだけだけど複数の設定項目が出た場合にグループとして扱えない、FooとFooDefaultValueは意味的にはペアとして扱いたいなど気になる点が出てきます。
文字列の列挙体があればいいのだけど、それは言語的にできません。ただし、じゃんぬさんが昔に文字列の列挙体というエントリを投稿されてます。詳しくはそちらで。私もこれを参考にして、ペアの部分についても考えて出たコードが以下。
まずは、値の設定・取得するところは次のようになって、
Dim foo = Options.GetString(FooPluginOptions.Foo.Name, FooPluginOptions.Foo.DefaultValue)
Options.SetString(FooPluginOptions.Foo.Name, "bar")
設定項目ごとに記述する部分は次のようになりました。
Public NotInheritable Class FooPluginOptions
Public Shared ReadOnly Foo As New Item(Of String)("foo")
Public Shared ReadOnly Bar As New Item(Of Integer)(100)
' (略 見てはいけない部分)
End Class
設定項目ごとに項目の型とデフォルト値を指定した、Itemというオブジェクトを作ってます。
隠された部分は以下。FooPluginOptionsの中にあるコードです。うーむ。設定項目のグループごとに書くとなると、どんだけーって感じですね。Itemクラスは外に出しても問題ありませんが、コンストラクタ内のコードは必要になり親クラスとして切り出すと動作しなくなってしまいます。
設定値の名前とそのデフォルト値のペアとしてItemクラスを作っています。プロパティとしてReadOnlyのNameとDefaultValueを持ってます。ToStringをオーバーライドしてNameを返すようにしているので Strict Offの場合はNameプロパティ要らず(私は常にStrict Onだけど)。Sharedなコンストラクタ内で自分自身のフィールドを取得してItemオブジェクトのNameプロパティにフィールド名そのままを設定してます。Private変数を書き変えてます。
Public NotInheritable Class Item(Of T)
Private _name As String
Public ReadOnly Property Name() As String
Get
Return _name
End Get
End Property
Private _defaultValue As T
Public ReadOnly Property DefaultValue() As T
Get
Return _defaultValue
End Get
End Property
Public Sub New(ByVal defaultValue As T)
_defaultValue = defaultValue
End Sub
Public Overrides Function ToString() As String
Return _name
End Function
End Class
Shared Sub New()
Dim fieldsInfo = GetType(BasePropertyItems).GetFields( _
BindingFlags.DeclaredOnly Or _
BindingFlags.GetField Or _
BindingFlags.Instance Or _
BindingFlags.Public Or _
BindingFlags.Static Or _
BindingFlags.FlattenHierarchy)
For Each info In fieldsInfo
Dim item = info.GetValue(Nothing)
item.GetType.InvokeMember( _
"_name", _
BindingFlags.SetField Or BindingFlags.NonPublic Or BindingFlags.Instance, _
Nothing, _
item, _
New Object() {info.Name}, _
Nothing, Nothing, Nothing)
Next
End Sub
どんな感じですかねぇ。ナイスな方法があれば教えてください。