PowerShell でカスタムオブジェクトを定義する一番ストレートな方法はハッシュテーブルと Add-Member を組み合わせるパターンでしょう。

$custom = [pscustomobject] @{ Message = 'Hello, world!' } |
  Add-Member -PassThru -MemberType ScriptMethod -Name Hello -Value {
    Write-Host -Foreground Green $this.Message
  }

この例は、Message というプロパティと、その値をホストに緑色で表示する Hello というメソッドをもったカスタムオブジェクトを定義しています。少し複雑な処理をするときなどは、このようなカスタムオブジェクトを積極的に活用する機会が多くなりますが、いかんせん記述が煩雑なことに加えて、メンバーのスコープを制御できないという欠点があります。

そんな場合は、動的モジュールを利用してカスタムオブジェクトを定義するといいですよ、というのが今回のネタです。

動的モジュールとは、その名のとおり、メモリ空間内に動的に生成されるモジュールのことで、New-Module を用いて生成します。試しに、動的モジュールをひとつ定義してみましょう。

New-Module {
  $Message = 'Hello, world!'
  function Hello { Write-Host -Foreground Green $Message }
}

これを実行するとモジュールが動的に生成され、モジュール内で定義したメンバーがセッションにインポートされ、利用可能になります。

これだけだと動的にモジュールを定義できました、というだけの話であまり面白くありません。興味深くなるのは New-Module に -AsCustomObject オプションを指定してからです。

$custom = New-Module  {
   …
} -AsCustomObject

このオプションを指定すると、エクスポートしたモジュールメンバーは現在のセッションのスコープにインポートされなくなります。そのかわり、モジュールのオブジェクトのメンバーとしてアクセスできるようになります。

$custom.Hello()

ここで気づかれたかと思いますが、このように生成した動的モジュールのインスタンスの振る舞いはカスタムオブジェクトそのものです。しかも、Export-ModuleMember を利用することで、メンバー変数や関数のスコープを自由にコントロールできるという利点があります。たとえば、モジュールで定義した変数は、デフォルトではエクスポートされません(上記の例でいうと$Messageという変数)。エクスポートさせるには以下のように明示的に指示する必要があります。

$custom = New-Module  {
   …
  Export-ModuleMember -Function Hello
  Export-ModuleMember -Variable Message

} -AsCustomObject

Export-ModuleMember を用いて明示的に Message をエクスポートしていることに加え、先ほどは暗黙的にエクスポートされていた Hello も明示的にエクスポートしている点に注意してください。明示的なエクスポートを用いる場合、公開したいメンバーを明示的にすべてエクスポートする必要があります。

モジュール定義にもちいるスクリプトブロックは、scriptblockのインスタンスであることには変わりないので、paramを用いて引数をとることができます。これを利用すると、以下のようなコンストラクター的関数を定義することも可能です。

function New-Greeter {
  param ([string] $ToWhom = 'World')
  New-Module {
    param ([string] $ToWhom)
    $Message = "Hello, $ToWhom!"
    Function Hello { Write-Host -Foreground Green $Message }
  } -AsCustomObject -ArgumentList $ToWhom
}

というわけで、New-Module を用いたカスタムオブジェクトの定義方法について簡単にまとめてみました。大規模なスクリプトを書くときにカスタムオブジェクトは非常に有用なので、ぜひ利用を検討してみてください。

明日は αετος さんの担当です。お楽しみに!