PMPの流儀

我が家の流儀

我が家で気になったことを夫婦で取り上げていきます

MENU

PowerShell 関数に複数の引数を参照渡し

PowerShellは一癖ある言語で、文法が少し違うのため戸惑う事が多いです。関数に参照渡しの引数を使うのも一苦労。
新しい言語ではこういう基本的なところではまってしまうんですよね。

STEP1

参照に ref をつけるのは、C#等と同じ概念です。こんな感じでコールすると

Function fnc([int]$x, [int]$y, [ref][int]$w, [ref][int]$z)
{
    if($y -ne 0) {
        $w = $x / $y
        $z = $x * $y
        Return $true
    } else {
        Return $false
    }
}

[int]$z = 0
[int]$w = 0
[Boolean]$ret = fnc 10 5 [ref]$w [ref]$z
$ret
$w
$z

実行結果

fnc : パラメーター 'w' の引数変換を処理できません。値 "[ref]0" を型 "System.Int32" に変換できません。エラー: "入力文字列の形式が正しくありません。"
発生場所 C:\temp\test.ps1:19 文字:26
+ [Boolean]$ret = fnc 10 5 [ref]$w [ref]$z
+                          ~~~~~~~
    + CategoryInfo          : InvalidData: (:) [fnc]、ParameterBindingArgumentTransformationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,fnc

呼び出し方に問題があるようです。

STEP2

規則1: 引数の参照渡し時には丸括弧で囲む 例: ([ref]$w)

注意点は fnc( 10, 5, [ref]$w, [ref]$z) ではなく、fnc 10 5 ([ref]$w) ([ref]$z) と引数1つ1つを括弧で括ることです。あと、引数の区切りはスペースであり、カンマではない点も注意してください。関数の定義はカンマを使っているのに呼び出し時は使ってはいけないというのは違和感を感じますが、慣れるしかありません。

では、実行してみましよう。

Function fnc([int]$x, [int]$y, [ref][int]$w, [ref][int]$z)
{
    if($y -ne 0) {
        $w = $x / $y
        $z = $x * $y
        Return $true
    } else {
        Return $false
    }
}

[int]$z = 0
[int]$w = 0
[Boolean]$ret = fnc 10 5 ([ref]$w) ([ref]$z)
$ret
$w
$z

実行結果

True
0
0

あれ、エラーにならないのに値が入らない。参照しているはずなのに。。。
初めて遭遇したときは謎の仕様に使うのを諦めてしまいました・・・
こういう所でつまづくとやる気を無くしてしまいますね。

STEP3

規則2: 参照した引数に値をセットするときは、Valueプロパティを使う。例: $w.Value = 10

違和感ありあり。[ref][int]$w なのでint型なのに、Valueプロパティなんてあるの ?
それがあるんですよ。$w.GetType() で型情報を調べてみると・・・

$w.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                                                       
-------- -------- ----                                     --------                                                                                                                                                       
True     True     Int32                                    System.ValueType    

このようにobjectなんですよ。そして、Value というプロパティがあります。では、気を取り直して以下を実行してみます。

Function fnc([int]$x, [int]$y, [ref][int]$w, [ref][int]$z)
{
    if($y -ne 0) {
        $w.Value = $x / $y
        $z.Value = $x * $y
        Return $true
    } else {
        Return $false
    }
}

[int]$z = 0
[int]$w = 0

[Boolean]$ret = fnc 10 5 ([ref]$w) ([ref]$z)

$ret
$w
$z

実行結果

True
2
50

やっとうまくいきました。サンプルで試すと理解できます。