2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PowerShellの配列で代入とshallow copyとDeepCopyと

Last updated at Posted at 2022-02-27

PowerShellで配列を操作する際に割とハマりがち? な気がする。
参照だったりShallowCopyだったりDeepCopyの話。

参照? Shallo Copy? Deep Copy?

まず前提として。

  • PowerShellで配列を代入した場合は参照がコピーされる
  • PowerShellで配列のcloneメソッドを利用するとShallowコピーされる

これはどういうことかというとサンプルをやってみるのが早い。

代入すると参照

# 元となる配列の宣言
$fromVariable=@("one","two","three","four","five")
# fromVariableをtoVariable変数に代入
$toVariable=$fromVariable

# toVariable変数を変更
$toVariable[0]="hello"
$toVariable[1]="world"

# 配列を表示してtoVariableへの変更がfromVariableに反映されていることの確認
# hello world three four five と表示
$fromVariable | %{ $_ }

# hello world three four five と表示
$toVariable | %{ $_ }

上記のようにPowerShellでは配列を変数に代入すると参照がコピーされます。
参照がコピーされるので、参照先を変更すると参照元も変更されます。

Cloneメソッドを利用するとshallow Copy

PowerShellの配列にはCloneメソッドが実装されています。
これを利用するとshallow Copyされます。

# 元となる配列の宣言
$fromVariable=@("one","two","three","four","five")
# toVariable変数に代入
$toVariable=$fromVariable.clone()

# toVariable変数を変更
$toVariable[0]="hello"
$toVariable[1]="World"

# 配列を表示してtoVariableへの変更がfromVariableに反映されていることの確認
# one two three four five
$fromVariable | %{ $_ }

# hello world three four five
$toVariable | %{ $_ }

先ほどの代入とは異なり、shallowCopy(簡易コピー)なので更に参照を含まないようなフラットな配列では。
参照先(toVariable)への変更が参照元(fromVariable)には影響していない事がわかります。

ただし下記のような配列の中で更に参照している場合は、shallowCopy(簡易コピー)なので参照先(toVariable)への変更が参照元(fromVariable)へ影響してしまいます。

$foobar=[pscustomObject]@{"foo"="bar"}

# 元となる配列の宣言
$fromVariable=@($foobar,"hello","world")
# toVariable変数にshallowCopy
$toVariable=$fromVariable.clone()

# toVariable配列の先頭にあるpscustomObjectの書き換え
$toVariable[0].foo= "helloworld"

# 配列を表示してtoVariableへの変更がfromVariableに反映されていることの確認
# @{foo=helloworld} hello world ※元々は@{foo=bar} だったのが書き換えられている。
$fromVariable | %{ $_}

# @{foo=helloworld} hello world
$toVariable | %{ $_}

Deep Copyしたい

配列の中に更に参照を含むような入れ子になっている配列について。
deep copyしたい場合のトリック。

Deep copy arrays in PowerShell

上記ページに記載のあるように、シリアライズしてからデシリアライズするというトリックでほぼ対応できるようです。

$rawFoobar=[pscustomObject]@{"foo"="bar"}
$foobar=$rawFoobar
# 元となる配列の宣言
$fromVariable=@($foobar,"hello","world")
# toVariable変数にshallowCopy
$toVariable=[Management.Automation.PSSerializer]::DeSerialize([Management.Automation.PSSerializer]::Serialize($fromVariable))

# toVariable配列の先頭にあるpscustomObjectの書き換え
$toVariable[0].foo= "helloworld"

# 配列を表示してtoVariableへの変更がfromVariableに反映されていることの確認
# @{foo=bar} hello world
$fromVariable | %{ $_}

# @{foo=helloworld} hello world
$toVariable | %{ $_}

ほぼ対応できる?

参照元のページにほぼ対応できると記載があり、Bigintについて言及されています。
たしかにbigintが含まれる配列をこの手法で使うと下記のようになりました。

# bigintを含む配列を用意
$fromVariable=@([bigint]100,[int]200,[int]300)
# deep copy
$toVariable=[Management.Automation.PSSerializer]::DeSerialize([Management.Automation.PSSerializer]::Serialize($fromVariable))

# typeをチェック
$fromVariable | %{ $_.gettype() }
# typeをチェック
$toVariable | %{ $_.gettype() }

#比較してみる
$fromVariable[0] -eq $toVariable[0]
$fromVariable[1] -eq $toVariable[1]
$fromVariable[2] -eq $toVariable[2]

image.png

画像のようにbigintがpsobjectになっていることがわかります。

総評

個人的にはPowerShellで配列を扱っている時に、知らずやらかしがちな部分かとは思います。

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?