.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

Unity+app.configでHello, wolrd

app.configを使ってIoC(制御の反転)をしてみます。本当はもっと複雑なことをしたいのですが、意外とconfigファイルを作成するのに手間取ったのでまずは簡単な例を作って注意点を探ります。

前準備

NuGetから「Unity」をインストールしておいてください。

Main.vb

Imports Microsoft.Practices.Unity
Imports Microsoft.Practices.Unity.Configuration
Imports Microsoft.Practices.ServiceLocation

Module Module1
    Sub Main()
        '注意!
        'LoadConfigurationは拡張メソッドなので、
        'Imports Microsoft.Practices.Unity.Configuration
        'をしないと利用できません。
        Dim container As New UnityContainer
        container.LoadConfiguration()

        Dim service As New UnityServiceLocator(container)
        ServiceLocator.SetLocatorProvider(Function() service)

        Dim p = ServiceLocator.Current.GetInstance(Of IPerson)()
        Console.WriteLine(p.Say())

        Console.WriteLine("何かキーを押してください")
        Console.ReadKey()
    End Sub
End Module

Public Interface IPerson
    Function Say() As String
End Interface

Public Class Person
    Implements IPerson

    Public Function Say() As String Implements IPerson.Say
        Return "Hello, wolrd."
    End Function
End Class

簡単にIoCなところを説明するとDim p = ServiceLocator.Current.GetInstance(Of IPerson)()と書いた後、インスタンスもせずにConsole.WriteLine(p.Say())とかメソッドを読んでるところ。VBのコードを追っかけても、pに何がはまっているのか全くわかりませんね。*1これは後述する「app.config」を見るとわかりますので、しばし無視しましょう。

では本題である注意点を列挙。

  • コメントにも書いてありますが、ポイントはImports Microsoft.Practices.Unity.Configurationコレ。configファイルで制御したいなら名前空間のインポートを忘れずに。
  • ソースコードには書いてありませんが、このアプリの「名前空間」と「アセンブリ」は「UnityConfigSample」です。名称は任意でよいのですが、名前空間と、アセンブリ名はあとで使うので覚えておきましょう。

本題ではないですが、上記のサンプルはServiceLocatorを使用してインスタンスを取り出しています。これはシングルトンで実装されてますので、どこからでもServiceLocator.Current.GetInstance(Of T)とすることでインスタンスを生成することができます。コンソールアプリではUnityServiceLocatorを直利用すればいいと思いますが、複数画面があるアプリの場合はServiceLocatorを使用するとよいでしょう。

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>

  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <container>
      <register type="UnityConfigSample.IPerson, UnityConfigSample" mapTo="UnityConfigSample.Person, UnityConfigSample" />
    </container>
  </unity>

</configuration>

ここのregisterセクションにて、「IPersonの実体はPersonだ」と指定しています。

ソースコードの外で指定しているのがミソ。このコード自体の実用性はありませんが、これがわからないと次に進めないのでそういうもんかと思ってください。私も勉強中ですので…

configSectionsセクション

configSectionsにおまじない<section name="unity"…を書く。 気を付けないといけないのは、他のサイトからコピペしたとき、

<!--注意 この記述だと今のUnity(2.1.505.0)では動きません-->
<section name="unity" type=
       "Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
       Microsoft.Practices.Unity.Configuration, Version=1.1.0.0,
       Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

こう書いてあることがあります。これは「Unityのバージョンは1.1.0.0」と指定しているので、Unityのバージョンに違いがあるとこけます*2。省略するとバージョン縛りはありませんので、省略をするか、適切なバージョン番号を指定してください。

unityセクション

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container>…</container></unity>は決まり文句だと思ってください。とりあえずそのまま書く。

注意すべき個所はregister。ここにインターフェイスと、マッピングする実体クラスを指定します。見ればなんとなくわかりますね。書式が「型正式名, アセンブリ」であることに注意してください。 以下のように書いても動きません。

<!--駄目な例1-->
<register type="IPerson" mapTo="Person" />
<!--駄目な例2-->
<register type="UnityConfigSample.IPerson" mapTo="UnityConfigSample.Person" />

System.InvalidOperationException はハンドルされませんでした。
Message=The type name or alias IPerson could not be resolved. Please check your configuration file and verify this type name.

ちゃんと書きましょう。

<!--正しい書式-->
<register type="UnityConfigSample.IPerson, UnityConfigSample" mapTo="UnityConfigSample.Person, UnityConfigSample" />

registerの書き方(別法)

実はregisterの書き方は何通りかあります。他のサイトを参考にした際、下記の記述を使用している場合がありますので、こっちの書き方も知っておきましょう。

alias

別名を付けることができます。

  <!--こう書いてもいい-->
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="IPerson" type="UnityConfigSample.IPerson, UnityConfigSample" />
    <alias alias="Person" type="UnityConfigSample.Person, UnityConfigSample" />
    <container>
      <register type="IPerson" mapTo="Person" />
    </container>
  </unity>
  <!--alias name は(わかりやすいかはともかく)任意の名前でよい-->
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <alias alias="hoge" type="UnityConfigSample.IPerson, UnityConfigSample" />
    <alias alias="fugo" type="UnityConfigSample.Person, UnityConfigSample" />
    <container>
      <register type="hoge" mapTo="fugo" />
    </container>
  </unity>

namespace, assembly

検索対象となる名前空間アセンブリを登録しておくことで記述を省略できます。

  <!--こう書いてもいい-->
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <namespace name="UnityConfigSample" />
    <assembly name="UnityConfigSample" />    
    <container>
      <register type="IPerson" mapTo="Person" />
    </container>
  </unity>

参考サイト

Using Design-Time Configuration

The Unity Configuration Schema

*1:IPersonを実装したクラスは1個しかないから、推測はできちゃいますけど。

*2:現在のバージョンは2.1.505.0でしたので、まずこけます。

. .