型変換について

簡単な説明

PowerShell には、使いやすくする柔軟な型システムがあります。 ただし、予期しない結果を回避するには、そのしくみを理解する必要があります。

長い説明

既定では、PowerShell 変数は、型制約されません。 1 つの型のインスタンスを含む変数を作成し、後で他の型の値を割り当てることができます。 また、PowerShell では、明示的にも暗黙的にも、値が自動的に他の型に変換されます。 暗黙的な型変換は役に立ちますが、特に、より厳密な型処理を持つ言語に慣れているユーザーには、落とし穴があります。

型制約変数と明示的な型変換

変数を型制約するには、代入の変数名の左側に型リテラルを配置します。 例えば:

[int]$foo = 42

型キャストを使用して、値を特定の型に明示的に変換できます。 例えば:

PS> $var = [int]'43'
PS> $var.GetType().Name
Int32

型の制約により、指定した型の値のみを変数に割り当てることができます。 制約付き型に変換できる別の型の値を割り当てようとすると、PowerShell は暗黙的な変換を実行します。 詳細については、この記事の「暗黙的な型変換 」セクションを参照してください。

数値型の変換

ターゲット型が変換された値を保持できる限り、数値型は他の任意の数値型に変換できます。 例えば:

PS> (42.1).GetType().Name
Double
PS> $byte = [byte] 42.1
PS> $byte
42
PS> $byte.GetType().Name
Byte

42.1 値は、Doubleです。 これを バイトにキャストすると、PowerShell は整数 42に切り捨てます。これは、バイトに収まるのに十分な小ささです。

実数を整数型に変換する場合、PowerShell では切り捨てではなく四捨五入が使用されます。具体的には、丸めから最も近い偶数 メソッドを使用します。 次の例は、この動作を示しています。 どちらの値も、最も近い偶数の整数 (22) に丸められます。

PS> [byte]21.5
22
PS> [byte]22.5
22

詳細については、Math.Round メソッドの 中間値と丸め規則 セクションを参照してください。

ブール型変換

任意の 型 値を ブールに強制変換できます。

  • 数値型の場合、0$false に変換され、その他の値は $trueに変換されます。

    PS> [boolean]0
    False
    PS> [boolean]0.0
    False
    PS> [boolean]-1
    True
    PS> [boolean]1
    True
    PS> [boolean]42.1
    True
    
  • その他の型の場合、null 値、空の文字列、および空の配列は、$falseに変換されます。

    PS> [boolean]''
    False
    PS> [boolean]@()
    False
    PS> [boolean]'Hello'
    True
    

    空のハッシュテーブルを含むその他の値は、$trueに変換されます。 単一要素コレクションは、1 つの要素のみのブール値に評価されます。 1 つ以上の要素を持つコレクションは常に $true

    PS> [boolean]@(0)
    False
    PS> [boolean]@(0,0)
    True
    PS> [boolean]@{}
    True
    

文字列型の変換

任意の 型 値を、文字列に強制変換できます。 既定の変換では、オブジェクトに対して ToString() メソッドを呼び出します。

配列は文字列に変換されます。 配列内の各要素は、個別に文字列に変換され、結果の文字列に結合されます。 既定では、変換された値はスペースで区切られます。 区切り記号は、$OFS 基本設定変数を設定することで変更できます。

PS> [string] @(1, 2, 3)
1 2 3

$OFSの詳細については、「about_Preference_Variables」を参照してください。

型が静的な Parse() メソッドを実装している場合は、1 つの文字列値を型のインスタンスに変換できます。 たとえば、[bigint]'42'[bigint]::Parse('42', [cultureinfo]::InvariantCulture)と同じです。 省略可能な [cultureinfo]::InvariantCulture 値は、メソッドの IFormatProvider 型パラメーターにバインドされます。 これにより、変換のカルチャに依存しない動作が保証されます。 Parse() メソッドのすべての実装にこのパラメーターがあるわけではありません。

手記

通常、文字列との間の変換は、インバリアント カルチャを使用して実行されます。 インバリアント カルチャは、US-English カルチャに基づいていますが、同じではありません。 特に、既定では、ピリオド (.) を 10 進記号として使用し、米国スタイルの月の最初の日付を使用します。 ただし、バイナリ コマンドレット は、パラメーター バインド中にカルチャに依存する変換を実行します。

列挙型の変換

PowerShell では、列挙型String インスタンスに変換したり、戻したりすることができます。 たとえば、typecast 文字列 [System.PlatformId]'Unix' は、[System.PlatformId]::Unix列挙値と同じです。 また、PowerShell は、文字列内のコンマ区切り値または文字列の配列として、フラグベースの列挙型を正しく処理します。 次の例を考えてみましょう。

[System.Reflection.TypeAttributes]'Public, Abstract'
[System.Reflection.TypeAttributes]('Public', 'Abstract')

次の例は、列挙型の式と同じです。

[System.Reflection.TypeAttributes]::Public -bor
    [System.Reflection.TypeAttributes]::Abstract

その他の型変換

次の場合、単一の値 (配列以外) を型のインスタンスに変換できます。

  • その型には、(パブリック) 単一パラメーター コンストラクターがあります
  • 値が同じ型であるか、パラメーターの型に強制的に適用できます

たとえば、次の 2 行は同等です。

[regex]'a|b'
[regex]::new('a|b')`

暗黙的な型

PowerShell では、リテラル値に型も自動的に割り当てられます。

数値リテラルは、既定で暗黙的に型指定されます。 数値は、そのサイズに基づいて入力されます。 たとえば、42Int32 型として格納するのに十分小さく、1.2Doubleとして格納されます。 [int32]::MaxValue より大きい整数は、Int64として格納されます。 42バイト として格納でき、1.2Single 型として格納できますが、暗黙的な型指定では、それぞれ Int32double が使用されます。 詳細については、「about_Numeric_Literals」を参照してください。

リテラル文字列は、文字列として暗黙的に型指定されます。 単一文字 文字列 インスタンスは、Char 型との間で変換できます。 ただし、PowerShell にはリテラル リテラルChar 型がありません。

暗黙的な型変換

特定のコンテキストでは、PowerShell は値を他の型に暗黙的に変換できます。 これらのコンテキストは次のとおりです。

  • パラメーター 紐付け
  • 型制約付き変数
  • 演算子を使用した式
  • ブール型のコンテキスト - PowerShell は、前述のように、ifwhiledo、または switchステートメントの条件式をBoolean 値に変換します。 詳細については、about_Booleansを参照してください。
  • 拡張型システム (ETS) 型定義 - 型変換は、いくつかの方法で定義できます。

パラメーターバインディングの変換

PowerShell は、パラメーターに渡された値をパラメーター型に一致するように変換しようとします。 パラメーター値の型変換は、パラメーターが特定の型で宣言されているコマンドレット、関数、スクリプト、スクリプトブロック、または .NET メソッドで発生します。 [Object] 型でパラメーターを宣言するか、特定の型を定義しない場合は、任意の値型をパラメーターに渡すことができます。 パラメーターには、ArgumentTransformationAttribute 属性でパラメーターを修飾することで、カスタム変換を定義することもできます。

詳細については、about_Parameter_Bindingを参照してください。

パラメーター バインドのベスト プラクティス

.NET メソッドの場合は、必要に応じ型キャストを使用して予想される正確な型を渡す方が適切です。 正確な型がない場合、PowerShell は間違ったメソッド オーバーロードを選択できます。 また、.NET の将来のバージョンで追加された新しいメソッド オーバーロードは、既存のコードを中断する可能性があります。 この問題の極端な例については、この Stack Overflow の質問を参照してください。

配列を [string] 型指定されたパラメーターに渡すと、PowerShell によって、前述のように配列が文字列に変換される場合があります。 次の基本的な関数について考えてみましょう。

function Test-String {
    param([string] $String)
    $String
}

Test-String -String 1, 2

配列が文字列に変換されるため、この関数は 1 2 出力します。 この動作を回避するには、 属性を追加して、[CmdletBinding()] を作成します。

function Test-String {
    [CmdletBinding()]
    param([string] $String)
    $String
}

Test-String -String 1, 2

高度な関数の場合、PowerShell は配列を非配列型にバインドすることを拒否します。 配列を渡すと、PowerShell は次のエラー メッセージを返します。

Test-String:
Line |
   7 |  Test-String -String 1, 2
     |                      ~~~~
     | Cannot process argument transformation on parameter 'String'. Cannot
     | convert value to type System.String.

残念ながら、.NET メソッド呼び出しではこの動作を回避できません。

PS> (Get-Date).ToString(@(1, 2))
1 2

PowerShell は、配列を文字列 "1 2"に変換します。この文字列は、 メソッドの ToString() パラメーターに渡されます。

次の例は、配列変換の問題の別のインスタンスを示しています。

PS> $bytes = [byte[]] @(1..16)
PS> $guid = New-Object System.Guid($bytes)
New-Object: Cannot find an overload for "Guid" and the argument count: "16".

$bytes はバイトの配列であり、$bytes には System.Guid コンストラクターがある場合でも、Guid(byte[]) 配列は個々のパラメーターのリストとして扱われます。

この一般的なコード パターンは、擬似メソッド構文のインスタンスであり、常に意図したとおりに動作するとは限りません。 この構文は次のように変換されます。

PS> [byte[]] $bytes = 1..16
PS> New-Object -TypeName System.Guid -ArgumentList $bytes
New-Object: Cannot find an overload for "Guid" and the argument count: "16".

ArgumentList の型が [Object[]] である場合、任意の型の配列としての 1 つの引数が、要素ごとに、バインドされます。 回避策は、外側の配列 $bytes ラップして、PowerShell が外側の配列の内容に一致するパラメーターを持つコンストラクターを探すようにすることです。

PS> [byte[]] $bytes = 1..16
PS> $guid = New-Object -TypeName System.Guid -ArgumentList (, $bytes)
PS> $guid

Guid
----
04030201-0605-0807-090a-0b0c0d0e0f10

ラップされた配列の最初の項目は、元の [byte[]] インスタンスです。 この値は、Guid(byte[]) コンストラクターと一致します。

組み込みの静的 new() メソッドを使用することが、配列ラップの回避策の代替となります。

PS> [byte[]] $bytes = 1..16
PS> [System.Guid]::new($bytes)  # OK

Guid
----
04030201-0605-0807-090a-0b0c0d0e0f10

型制約付き変数変換

型制約付き変数に値を割り当てると、PowerShell は値を変数型に変換しようとします。 指定された値を変数型に変換できる場合、割り当ては成功します。

例えば:

PS> [int]$foo = '43'
PS> $foo.GetType().Name
Int32

文字列 '43' を数値に変換できるため、変換が機能します。

演算子の変換

PowerShell では、式のオペランドを暗黙的に変換して、妥当な結果を生成できます。 また、一部の演算子には型固有の動作があります。

数値演算

数値演算では、両方のオペランドが同じ数値型であっても、結果に対応する自動型変換により、結果が異なる型になる可能性があります。

PS> [int]$a = 1
PS> [int]$b = 2
PS> $result = $a / $b
PS> $result
0.5
PS> $result.GetType().Name
Double

両方のオペランドが整数であっても、結果は小数部の結果をサポートするために Double に変換されます。 真の整数除算を取得するには、[int]::Truncate() または [Math]::DivRem() 静的メソッドを使用します。 詳細については、「Truncate() と divRem()を参照してください。

整数演算では、結果がオペランドのサイズをオーバーフローすると、結果が Int64 型に収まる場合でも、PowerShell は既定で結果に Double を使用します。

PS> $result = [int]::MaxValue + 1
PS> $result
2147483648
PS> $result.GetType().Name
Double

結果を Int64にする場合は、結果の型またはオペランドをキャストできます。

PS> ([int64]([int]::MaxValue + 1)).GetType().Name
Int64

ただし、特定の型に結果をキャストする際には注意を払う必要があります。 たとえば、結果を [decimal] 型にキャストすると、精度が失われる可能性があります。 最大 1 値に を追加すると、Double 型になります。 DoubleDecimal 型にキャストすると、結果は 9223372036854780000になり、正確ではありません。

PS> ([int64]::MaxValue + 1).GetType().Name
Double
PS> [decimal]([int64]::MaxValue + 1)
9223372036854780000

変換の有効桁数は 15 桁に制限されています。 詳細については、Decimal(Double) コンストラクタードキュメントの「解説」セクションを参照してください。

精度の損失を回避するには、D リテラルで 1 サフィックスを使用します。 D サフィックスを追加すると、PowerShell は [int64]::MaxValueを追加する前に 1D に変換します。

PS> ([int64]::MaxValue + 1D).GetType().Name
Decimal
PS> ([int64]::MaxValue + 1D)
9223372036854775808

数値サフィックスの詳細については、about_Numeric_Literalsを参照してください。

通常、PowerShell 演算子の左側 (LHS) オペランドによって、操作で使用されるデータ型が決まります。 PowerShell は、右側 (RHS) オペランドを必要な型に変換 (強制) します。

PS> 10 - ' 9 '
1

この例では、RHS オペランドは、減算演算の前に暗黙的に整数に変換される文字列 ' 9 'です。 比較演算子についても同様です。

PS> 10 -eq ' 10'
True
PS> 10 -eq '0xa'
True

arithmetic_演算子 (+-*/) を数値ではない オペランドで使用する場合に例外があります。

文字列で - オペランドと / オペランドを使用すると、PowerShell は両方のオペランドを文字列から数値に変換します。

PS> '10' - '2'
8
PS> '10' / '2'
5

これに対し、+ 演算子と * 演算子には、文字列固有のセマンティクス (連結とレプリケーション) があります。

PS> '10' + '2'
102
PS> '10' * '2'
1010

算術演算子 ブール 値を使用すると、PowerShell は値を整数に変換します。$true[int]1 になり、$false[int]0になります。

PS> $false - $true
-1

1 つの例外は、2 つのブール値の乗算 (*) です。

PS> $false * $true
InvalidOperation: The operation '[System.Boolean] * [System.Boolean]' is not
defined.

他の LHS 型の場合、算術演算子は、特定の型が、をオーバーロードする演算子 使用してこれらの演算子をカスタム定義する場合にのみ成功します。

比較操作

-eq-lt-gtなどの比較演算子では、さまざまな型のオペランドを比較できます。 非文字列型と非プリミティブ型の動作は、LHS 型が IEquatableIComparableなどのインターフェイスを実装するかどうかによって異なります。

コレクションベースの比較演算子 (-in および -contains) は、一致が見つかるまで、要素ごとに -eq 比較を実行します。 あらゆる型強制を決定するのは、コレクションオペランドの各要素です。

PS> $true -in 'true', 'false'
True
PS> 'true', 'false' -contains $true
True

'true' -eq $true$trueを生成するため、どちらの例も true を返します。

詳細については、about_Comparison_Operatorsを参照してください。