見出し画像

PowerShell関数の引数について参照渡し、参照の値渡し

前に書いた記事に関数のオブジェクト引数は参照ではないとコメントが付きました。
コメントの通りでした。

PowerShellで関数の引数を参照渡ししたい|🐹マリモのごはん🐍


もう一度

関数の引数を考えてみましょう。

呼出側と関数側は値のやり取りしかしていなくて、その値が数字なのか、参照値(メモリ上のアドレス、C言語で言うところのポインタ?)なのかの違いで、関数側がそれを引数変数にコピーして使用します。引数が値の場合は何も問題はありません。

引数が参照値の場合:
オブジェクト(配列など)を引数として受け渡した場合、関数側の引数変数には呼出側の参照値が入り、関数内で引数変数の値に変更をすると、呼出側の変数の値も変更されます。ただし、引数変数をまるごと別のオブジェクトで上書きすると呼出側への影響が無くなります。引数変数の参照値(メモリ上のアドレス、C言語で言うところのポインタ?)が別物になるからです、これらの引数の渡しを「参照の値渡し」と呼ばれ、参照渡しとは別として扱われています。

参照渡しとは

  • 正しい 参照渡し(pass by reference)

  1. オブジェクトの参照値(メモリ上のアドレス、ポインタ)を渡す

  2. 呼出側と関数側で紐付きされる

呼出元と関数側で完全に同じ引数を受け渡すこと。関数側で引数に加えられた変更は、呼出側の変数にも反映されます。

  • 参照の値渡し(pass by referenceValue)

  1. オブジェクトの参照値(メモリ上のアドレス、ポインタ)を渡す

  2. 呼出側と関数側で紐付きされない

関数側で引数を操作すると呼出側の変数にも反映される点は参照渡しと同じです。ただし、新しいオブジェクトを代入するなど参照値自体を変更すると新しい参照値は呼出側に反映されず参照渡しと違う動作になります。これは参照値の値を変更したことにより、それぞれ別物になるからです。


まずこれ

参照の値渡しの例1

function test($z){
    $z[0]+=1000
}


$a=@(1,2,3)
write-host "呼び出し前 $a "
test $a
write-host "呼び出し後 $a "

#実行結果
#呼び出し前 1 2 3 
#呼び出し後 1001 2 3 

配列$aを関数testで引数変数$zで受け取り、0番目を+1000しています。呼出側の配列$aも書き換わっています。
関数testでは配列$aの参照値(メモリ上のアドレス、C言語で言うところのポインタ?)を引数変数$zで受け取って扱っているので関数testで$zの値を更新すると呼出側も更新されます。この動作は参照渡しと同じです。

function test($z){
    $a=@(1001,2,3)
}


$a=@(1,2,3)
write-host "呼び出し前 $a "
test $a
write-host "呼び出し後 $a "

#実行結果
#呼び出し前 1 2 3 
#呼び出し後 1 2 3 

関数の中で引数変数$zを配列で上書きしました。呼出側には影響がでていません。これは引数変数$zの参照値(メモリ上のアドレス)を別のものに上書きしたからです。参照値の渡しを行っているのでこのようなことが起こります。呼出側と関数側で紐づけされていない証拠でもあります。
間違えやすいポイントです。関数内で書き換えたのにオブジェクトの内容が変わってない!と勘違いしそうです。

参照の値渡しの例2

function test($z){
    $z+=@(1000)
    write-host "関数の中 $z"
}

$a=@(1,2,3)
write-host "呼び出し前 $a "
test $a
write-host "呼び出し後 $a "

#実行結果
#呼び出し前 1 2 3 
#関数の中 1 2 3 1000
#呼び出し後 1 2 3 

関数の中で引数変数$zに要素を追加しました。呼出側には影響ありません。
PowerShellでは配列が固定長なので要素を追加すると配列を再作成されるため、配列の参照値(メモリ上のアドレス)が変わるのです。別物になるので呼出側には影響ありません。関数内で書き換えたのに内容が変わってない!と間違えそうです。


では参照渡しにしてみるとどうでしょうか
PowerShellは関数の引数に[ref]をつけると参照渡しになります。

function test([ref]$z){
    $z.value=@(1001,2,3)
}

$a=@(1,2,3)
write-host "呼び出し前 $a "
test([ref]$a)
write-host "呼び出し後 $a "

#実行結果
#呼び出し前 1 2 3 
#呼び出し後 1001 2 3 

関数の中で引数変数$z.valueを別のオブジェクトで上書きしました。呼出側の変数の値も書き換わっています。呼出側と関数側で紐づけがされているのでこのような動作になります。
$zが呼出側と同じオブジェクトで$z.valueが仮引数ということでしょうか。

もうひとつ参照

function test([ref]$z){
    $z.value+=@(500)
}

$a=@(1,2,3)
write-host "呼び出し前 $a "
test([ref]$a)
write-host "呼び出し後 $a "

#実行結果
#呼び出し前 1 2 3 
#呼び出し後 1 2 3 500 

関数内で引数変数$zに配列要素を追加しました。配列が再作成されるので参照値が変わっています。呼出側も要素が追加されています。
まぁ$zと$z.valueで分かれているので。


参照渡しで$zと$z.valueで分かれているなら辞書型で同じことができる?

function test($z){
    $z.value=@(1001,2,3)
}

$a=@{value=@(1,2,3)}
write-host "呼び出し前 $($a.value) "
test $a
write-host "呼び出し後 $($a.value) "

#実行結果
#呼び出し前 1 2 3 
#呼び出し後 1001 2 3 

できました。
辞書型で渡すと引数に[ref]をつけるとのソックリの動作になります。

なんか、もう、参照の値渡しだけでよくない?


#PowerShell #参照渡し #値渡し #プログラミング初心者 #プログラミング学習 #プログラミング

いいなと思ったら応援しよう!