tech.guitarrapc.cóm

Technical updates

PowerShellでHashtableを型付けしたい

どうも、動的型付なPowerShellです。 といっても型好きなPowerShellerです。 ということで、だれもがイラッとする型安全ではない(type-unsafe) Hashtableを型安全(type-safe)にできないかというのが今回の記事です。

Hashtableとは

ようは連想配列ですね。
MSDN - Hashtable クラス
基本的な使い方はHey, Scripting Guy! 様にお出まし願います。
Hey, Scripting Guy! Blog // Easily Create a PowerShell Hash Table
さて、今回の狙いはこのHashtableのName.Valueを型制約できないかという事を試します。

通常のHashtable

例えば以下のHashtable $employeeを考えます。 KeysがNO、Valueが123456 KeysがName、ValueがAlice これを作るには、@{K=V}や@{}の.Key=ValueとHashtableのプロパティ指定で作成するのが一般的かと思います。
# Week Typed Hash Table
$emploee = @{}
$emploee.NO = 123456
$emploee.Name = "Alice"
結果を見てみましょう
$emploee
$employee.NOキーのValueに123456が入ります。 $employee.NameキーのValueには"Alice"が入ります。
Name  : Name
Value : Alice

Name  : NO
Value : 123456
[PSCustomObject]にキャストすれば他のオブジェクト同様に使いやすくなりますね
Name      NO
----      --
Alice 123456

通常のHashtableは型安全ではない(type-unsafe)

先ほど、"Alise"としたValueの型を見てみると、
$emploee.Name.GetType().FullName
[string]型になっていますね
System.String
では、作った$employee{}.NameキーのValueに数値を入れてみましょう
$emploee.Name = 456
動的型付けにより、型変換エラーなく入っちゃいます。 出力してみましょう。
$emploee
[PSCustomObject]$emploee | Format-Table -AutoSize
Key   : NO
Value : 123456
NO Name
--  ----
123456  456
NameキーのValueの型を調べると、
$emploee.Name.GetType().FullName
[int]型になっていますね
System.Int32
このようにPowerShellの@{K=V}で作成したHashtableは、型制限がなく動的に型変換されます。

Hashtableを型制約して型安全(type-safe)にする

Hashtableは、System.Collections.Hashtableを生成しているわけです。 つまり、このコードでも@{}と同等のHashtableが生成されます。
$hash = @{}
$hashDotNet = New-Object 'System.Collections.Hashtable' #hashと一緒
ならば、作成時に、K,Vそれぞれの型を制約すればいいわけです。 そこで、GenericなDictionaryを使います。 イメージ的にはこうです。
System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
まず、Dictionaryを作ります。 今回は、Keyを[string]型で、Valueを[int]型とします。
$emploeeTyped = New-Object 'System.Collections.Generic.Dictionary[string, int]'
K,Vに値を入れてみましょう。
$emploeeTyped.NO = 123456
エラーなく上手く入ったようですので確認します。
$emploeeTyped
大丈夫ですね。
Key   : NO
Value : 123456
では、Vに[string]を入れてみます。
$emploeeTyped.Name = "Margaret"
[int]型には[string]型を入れれませんと、きっちりはじいてくれました。
値 "Margaret" を型 "System.Int32" に変換できません。エラー: "入力文字列の形式が正しくありません。"
発生場所 D:\Document\Program\Powershell\TypedHashtable\TypedHashtable.ps1:17 文字:1
+ $emploeeTyped.Name = "Margaret"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	+ CategoryInfo          : InvalidArgument: (:) []、RuntimeException
	+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
当然、NoのK,Vしか入っていません。
$emploeeTyped
結果です、
Key   : NO
Value : 123456
例えDisctionaryにしても,[PSCustomObject]へのキャストはできるので安心です。
[PSCustomObject]$emploeeTyped
出力です。
Key  Value
---  -----
NO  123456

まとめ

これで、Hashtableが型安全(type-safe)に生成できるようになりました。 [PSCustomObject]という便利なオブジェクト生成手段に利用するため、HashtableというよりDictionaryにして使い勝手を管理できると嬉しいですね。 ただ、PowerShellは動的型付なのでHashTableでも型エラーがでないので気にされにくいのでしょうか…今更ネタかなぁ…。

補足

LINQ星人の指摘で、Hashtableのクラス間違えていたので修正しました。