PowerShell で Generic なクラスを扱う場面は少ないと耳にします。
目次
使わない理由: PowerShellは動的型付け
PowerShellにおいて変数は、多くの場合 Objectとして動作します。
一方で、 値(int
や string
) に関しては、その変数に値を代入する際、変数の型に暗黙的にキャスト(変換)を試みます。
つまり、特に数値や文字列はVariantな動作もします。
この辺は、C# の人がわかりやすいです。
このため、あまり型変換を気にすることなくサクサクかけるのが、Generic を使わないとかいう理由に上がっているようです。
Genericを使う理由 : 厳密に型チェックしたい
当然、厳密な型指定したいときは Genericなクラス(System.Collections.Generic
) を利用したくなります。
ただコンパイルがないので、静的型付けチェックに難があるわけですが。
ともかく、今回は List<T>
を PowerShellで使ってみましょう。
msdn - List
クラス インデックスを使用してアクセスできる、厳密に型指定されたオブジェクトのリストを表します。
PowerShell で Listを扱う
List の生成
PowerShellでは、New-Object
を利用します。
仮に List<int>
で作成するならこうです。
# Create Generic List for int $list = New-Object 'System.Collections.Generic.List[int32]'
今回は、List<string>
で作成します。
# Create Generic List for string $list = New-Object 'System.Collections.Generic.List[string]'
Syste.Correction.Generic の生成をCmdlet風に行う
Genericを生成するCmdletを書いてみました。
このCmdletを利用すると、Cmdlet風にGeneric Classを生成できます。
# sample List<string> $list = New-ObjectGeneric -className List -typeParameters "string" # sample Dictionary<string,List<string>> $dic = New-ObjectGeneric -className Dictionary -typeParameters ("string",$list.GetType().FullName)
パラメーターを短縮も可能です。
# sample List<string> $list = New-ObjectGeneric List "string" # sample Dictionary<string,List<string>> $dic = New-ObjectGeneric Dictionary ("string", $list.GetType().FullName)
List.Add()
要素の追加は、そのままです。
# Add items to list $list.Add("a") $list.Add("b") $list.Add("c") $list.Add("d") $list.Add("a") $list.Add("a") $list.Add("a")
もちろん List<int>
の場合は、追加ができません。
# Add items to list $list = New-Object 'System.Collections.Generic.List[int32]' $list.Add("a")
Cannot convert argument "item", with value: "a", for "Add" to type "System.Int32": "Cannot convert value "a" to type "System.Int32". Error: "Input string was not in a correct format."" At line:1 char:1 + $list.Add("a") + ~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
List.AddRange()
配列の追加も可能です。
# Add range of items $list.AddRange( [string[]]@("e","f","g","h") ) # make sure type match for list
List.Find()
リスト全体から、条件に該当する一番小さなインデックス番号の要素を返します。
C# ならば、LINQ を使って簡単に書けます。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { var list = new List<string> { }; list.Add("a"); list.Add("b"); list.Add("c"); list.Add("d"); list.Add("e"); Console.WriteLine(list.Find(x => x == "a")); // a が出力される } } }
しかし、PowerShell には LINQ がないのです。残念ながら、本当に残念ながら。
そのため、匿名デリゲート
を作ってあげる必要があります。
当然これではだめですよ。
$list.Find("a") # return Error
PowerShell では、ScriptBlock{}
が匿名デリゲートに該当します。これを使って"System.Predicate`1[System.Int32]"
を生成します。
# find matched item of System.Predicate using ScriptBlock (anonymous delegates) $list.Find({$args -eq "a"}) # return a $list.Find({$args -ceq "A"}) # nothing return $list.Find({$args -eq "Z"}) # nothing return
List.FindLast()
リスト全体から、条件に該当する一番大きなインデックス番号の要素を返します。
Find()と同じ要領です。
# find from last for an item of System.Predicate using ScriptBlock (anonymous delegates) $list.FindLast({$args[0] -eq "c"}) # return c
$list.FindLast({$args[0] -eq "c"}) # return c
List.FindAll()
リスト全体から、条件に該当する要素を全て返します。
これに関してはFind()
、FindAdd()
と Where-Object
の両方が使えます。
# find all matched item of System.Predicate using ScriptBlock (anonymous delegates) $list.FindAll({$args[0] -eq "a"}) # find all a $list | where {$_ -eq "a"}
List.FindIndex()
リストに指定したindexから最後までで、条件に該当する一番小さなインデックス番号の要素を返します。
# find index for matched item from specific index to last index. item created is System.Predicate using ScriptBlock (anonymous delegates) $list.FindIndex(0,{$args[0] -eq "a"}) # retrun index 0 $list.FindIndex(1,{$args[0] -eq "a"}) # retrun index 5
List.FindLastIndex()
リストの先頭から指定したindexまでで、条件に該当する一番小さなインデックス番号の要素を返します。
# find index of item start from last record $list.FindLastIndex({$args[0] -eq "a"})
List.Insert()
指定したインデックスの位置に要素を追加します。
# insert item to specific index $list.Insert(2,"f") # f will insert to index 2
List
要素全体の取得は、そのままですね。
# list up current items $list
List.Remove()
リストで、条件に該当する一番小さなインデックス番号の要素を削除します。
# Remove method for first match item $list.Remove("a") # delete first a
List.RemoveAll()
リスト全体から、条件に該当する全ての要素を削除します。
# RemoveAt method to delete specific index item $list.RemoveAt(0) # delete b
List.RemoveAt()
指定したインデックスの要素を削除します。
# RemoveAt method to delete specific index item $list.RemoveAt(0) # delete b
List.FindLastIndex()
指定したインデックス範囲の要素を削除します。
# RemoveRange method to delete specific range items $list.RemoveRange(0,1) # delete c and d
List.Contains()
要素がリストに存在するかどうかを判定します。
# check item is exist or not $list.Contains("f") #true $list.Contains("a") #false
ToArray()
List<string を stringに変換するには、C# 同様の手法をとります。
たとえば、それぞれの要素を1つの文字列に変換し、各要素は ,(カンマ)
で区切るならこうです。
# output items into single String line with , foreach items $stringarray = $list.ToArray() $stringSeparateComma = [string]::Join(",",$stringarray) $stringSeparateComma # c,d,e,f,g,h
区切りなしで1つの文字列にするならこうです。
# output items into single String line with , foreach items $stringarray = $list.ToArray() $string = [string]::Join("",$stringarray) # No separate $string # cdefgh
Clear
リストの内容をクリアするならこうです。
# clear list $list.Clear()
まとめ
List いいですよ。 あとは、コンパイルを (お前は何をいっている
今回紹介したサンプルコードはGitHubにおいておきます。
GitHub - guitarrapc / PowerShellUtil / Handle-GenericList / Handle-GenericList.ps1