om_typomvandling

Kort beskrivning

PowerShell har ett flexibelt typsystem som gör det enklare att använda. Du måste dock förstå hur det fungerar för att undvika oväntade resultat.

Lång beskrivning

Som standardinställning är PowerShell variabler inte typbegränsade. Du kan skapa en variabel som innehåller en instans av en typ och senare tilldela värden av vilken annan typ som helst. Dessutom konverterar PowerShell automatiskt värden till andra typer, både explicit och implicit. Implicit typkonvertering kan vara till hjälp, men det finns fallgropar, särskilt för användare som är mer bekanta med språk som har striktare typhantering.

Typbegränsade variabler och explicit typkonvertering

Om du vill typbegränsa en variabel placerar du en typliteral till vänster om variabelnamnet i en tilldelning. Till exempel:

[int]$foo = 42

Du kan använda typgjutning för att explicit konvertera ett värde till en viss typ. Till exempel:

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

Typbegränsning säkerställer att endast värden av den angivna typen kan tilldelas till variabeln. PowerShell utför en implicit konvertering om du försöker tilldela ett värde av en annan typ som kan konverteras till den begränsade typen. Mer information finns i avsnittet implicit typkonvertering i den här artikeln.

Konvertering av numeriska typer

Numeriska typer kan konverteras till valfri annan numerisk typ så länge måltypen kan innehålla det konverterade värdet. Till exempel:

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

Värdet 42.1 är en Double. När du omvandlar den till en Byte, trunkerar PowerShell den till ett heltal 42, vilket är tillräckligt litet för att få plats i en Byte.

När du konverterar reella tal till heltalstyper använder PowerShell avrundning i stället för trunkering, särskilt med hjälp av metoden avrundning till närmaste jämna. Följande exempel illustrerar det här beteendet. Båda värdena avrundas till närmaste jämna heltal 22.

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

Mer information finns i avsnittet Midpoint-värden och avrundningskonventioner i metoden Math.Round.

Boolesk typkonvertering

Ett värde av vilken typ som helst kan omvandlas till en boolesk .

  • För numeriska typer konverteras 0 till $false och andra värden konverteras till $true.

    PS> [boolean]0
    False
    PS> [boolean]0.0
    False
    PS> [boolean]-1
    True
    PS> [boolean]1
    True
    PS> [boolean]42.1
    True
    
  • För andra typer konverteras null-värden, tomma strängar och tomma matriser till $false.

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

    Andra värden, inklusive tomma hashtabeller, konverteras till $true. Enelementssamlingar utvärderas till det booleska värdet för sitt enda element. Samlingar med fler än 1 element är alltid $true.

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

Konvertering av strängtyp

Ett värde av vilken typ som helst kan konverteras till en String. Standardkonverteringen är att anropa metoden ToString() på objektet.

Matriser konverteras till strängar. Varje element i matrisen konverteras till en sträng, individuellt och ansluten till den resulterande strängen. Som standard avgränsas de konverterade värdena med ett blanksteg. Avgränsaren kan ändras genom att ange $OFS inställningsvariabel.

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

Mer information om $OFSfinns i about_Preference_Variables.

Ett enda strängvärde kan konverteras till en instans av en typ om typen implementerar en statisk Parse() metod. Till exempel är [bigint]'42' samma som [bigint]::Parse('42', [cultureinfo]::InvariantCulture). Det valfria [cultureinfo]::InvariantCulture-värdet binder till en IFormatProvider typparameter för metoden. Det säkerställer konverteringens kulturinvarianta beteende. Det är inte alla implementeringar av Parse() metoder som har den här parametern.

Anmärkning

Konvertering till och från strängar utförs vanligtvis med hjälp av den invarianta kulturen. Invariant kultur baseras på, men inte identiskt med, US-English kultur. I synnerhet används perioden (.) som decimaltecken och månadsdatum i AMERIKANSK stil som standard. Men binära cmdletar utför kulturkänslig konvertering under parameterbindning.

Konvertering av uppräkningstyp

PowerShell kan konvertera Enum-typer till och från sträng--instanser. Typecast-strängen [System.PlatformId]'Unix' är till exempel samma som uppräkningsvärdet [System.PlatformId]::Unix. PowerShell hanterar även flaggbaserade uppräkningar korrekt för kommaavgränsade värden i en sträng eller som en matris med strängar. Tänk på följande exempel:

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

De här exemplen motsvarar enumuttrycket:

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

Andra typkonverteringar

Ett enda värde (icke-matris) kan konverteras till en instans av en typ om:

  • Den typen har en (offentlig) konstruktor med en parameter
  • Och värdet är av samma typ eller kan tvingas till parametertypen

Följande två rader är till exempel likvärdiga:

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

Implicita typer

PowerShell tilldelar också typer till literalvärden automatiskt.

Numeriska literaler skrivs implicit som standard. Tal skrivs baserat på deras storlek. 42 är till exempel tillräckligt litet för att lagras som en Int32- typ och 1.2 lagras som en Double. Heltal som är större än [int32]::MaxValue lagras som Int64. Även om 42 kan lagras som en Byte- och 1.2 kan lagras som en Single-typen, används implicit typning med Int32 respektive Double. Mer information finns i about_Numeric_Literals.

Literalsträngar typsätts implicit som String. Enteckensinstanser String kan konverteras till och från Tecken typ. PowerShell har dock ingen literal Char typ.

Implicit typkonvertering

I vissa sammanhang kan PowerShell implicit konvertera värden till andra typer. Dessa kontexter omfattar:

  • Parameterbindning
  • Typbegränsade variabler
  • Uttryck med operatorer
  • Booleska kontexter – PowerShell konverterar villkorsuttrycken för if, while, doeller switch-instruktioner till booleska värden enligt beskrivningen ovan. Mer information finns i about_Booleans.
  • Definitioner av typen Extended Type System (ETS) – Typkonverteringar kan definieras på flera sätt:

Parameterbindningskonverteringar

PowerShell försöker konvertera värden som skickas till parametrar för att matcha parametertypen. Typkonvertering av parametervärden sker i cmdletar, funktioner, skript, skriptblock eller .NET-metoder där parametern deklareras med en viss typ. Om du deklarerar en parameter med typen [Object] eller inte definierar en viss typ kan alla värdetyper skickas till en parameter. Parametrar kan också ha anpassade konverteringar definierade genom att dekorera parametrar med ArgumentTransformationAttribute attribut.

Mer information finns i about_Parameter_Binding.

Metodtips för parameterbindning

För .NET-metoder är det bättre att skicka exakt den typ som förväntas med hjälp av en typgjutning där det behövs. Utan exakta typer kan PowerShell välja fel metodöverlagring. Dessutom kan nya metodöverbelastningar som läggs till i framtida versioner av .NET bryta befintlig kod. Ett extremt exempel på det här problemet finns i den här Stack Overflow-frågan.

Om du skickar en matris till en [string] angiven parameter kan PowerShell konvertera matrisen till en sträng enligt beskrivningen ovan. Överväg följande grundläggande funktion:

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

Test-String -String 1, 2

Den här funktionen matar ut 1 2 eftersom matrisen konverteras till en sträng. Undvik det här beteendet genom att skapa en avancerad funktion genom att lägga till attributet [CmdletBinding()].

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

Test-String -String 1, 2

För avancerade funktioner vägrar PowerShell att binda matrisen till en icke-matristyp. När du skickar en matris returnerar PowerShell följande felmeddelande:

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

Tyvärr kan du inte undvika det här beteendet för .NET-metodanrop.

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

PowerShell konverterar matrisen till strängen "1 2", som skickas till parametern Format för metoden ToString().

I följande exempel visas en annan instans av matriskonverteringsproblemet.

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".

PowerShell behandlar den $bytes matrisen som en lista över enskilda parametrar även om $bytes är en matris med byte och System.Guid har en Guid(byte[]) konstruktor.

Det här vanliga kodmönstret är en instans av pseudometodsyntax, som inte alltid fungerar som avsett. Den här syntaxen översätts till:

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".

Eftersom typen av ArgumentList är [Object[]], binder ett enda argument som råkar vara en matris (av valfri typ) till den element efter element. Lösningen är att omsluta $bytes i en yttre matris så att PowerShell letar efter en konstruktor med parametrar som matchar innehållet i den yttre matrisen.

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

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

Det första objektet i den omslutna matrisen är vår ursprungliga [byte[]]-instans. Det värdet matchar konstruktorn Guid(byte[]).

Ett alternativ till matrisomslutningslösningen är att använda den inbyggda statiska metoden new().

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

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

Typbegränsade variabelkonverteringar

När du tilldelar ett värde till en typbegränsad variabel försöker PowerShell konvertera värdet till variabeltypen. Om det angivna värdet kan konverteras till variabeltypen lyckas tilldelningen.

Till exempel:

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

Konverteringen fungerar eftersom strängen '43' kan konverteras till ett tal.

Operatorkonverteringar

PowerShell kan implicit konvertera operanderna i ett uttryck för att skapa ett rimligt resultat. Vissa operatorer har också typspecifika beteenden.

Numeriska åtgärder

Även om båda operanderna är av samma numeriska typ i numeriska åtgärder kan resultatet vara en annan typ, på grund av automatisk typkonvertering för att hantera resultatet.

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

Även om båda operanderna är heltal konverteras resultatet till en Double för att stödja bråkresultatet. Använd de statiska metoderna [int]::Truncate() eller [Math]::DivRem() för att få en sann heltalsdivision. Mer information finns i Truncate() och DivRem().

Inom heltalsaritmetik, när resultatet överflödar storleken på operanderna, använder PowerShell standardmässigt Double för resultaten, även när resultatet kan passa i en Int64 typ.

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

Om du vill att resultatet ska vara en Int64kan du casta resultattypen eller operanderna.

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

Var dock försiktig när du omvandlar resultat till en specifik typ. Använd till exempel typecasting av resultatet till typen [decimal] kan leda till förlust av precision. Om du lägger till 1 till det maximala Int64--värdet resulterar det i en Double-typ. När du omvandlar en Double till en Decimal-datatyp blir resultatet 9223372036854780000, vilket inte är korrekt.

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

Konverteringen är begränsad till 15 siffror med precision. Mer information finns i avsnittet Kommentarer i Decimal(Double) konstruktordokumentation.

Använd D suffixet på den 1 literalen för att undvika att precisionen går förlorad. Genom att lägga till suffixet D konverterar PowerShell [int64]::MaxValue till en Decimal innan du lägger till 1D.

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

Mer information om numeriska suffix finns i about_Numeric_Literals.

Vanligtvis avgör LHS-operand (vänster sida) av PowerShell-operatorer den datatyp som används i åtgärden. PowerShell konverterar (tvingar) högersidans operand (RHS) till den nödvändiga typen.

PS> 10 - ' 9 '
1

I det här exemplet är RHS-operand strängen ' 9 ', som implicit konverteras till ett heltal före subtraktionen. Detsamma gäller för jämförelseoperatorerna.

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

Det finns undantag när du använder arithmetic_ operatorer (+, -, *, /) med icke-numeriska operander.

När du använder - och / operander med strängar konverterar PowerShell båda operanderna från strängar till tal.

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

Operatorerna + och * har däremot strängspecifika semantik (sammanfogning och replikering).

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

När du använder booleska värden med aritmetiska operatorer konverterar PowerShell värdena till heltal: $true blir [int]1 och $false blir [int]0.

PS> $false - $true
-1

Det enda undantaget är multiplikationen (*) för två booleska värden.

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

För andra LHS-typer lyckas aritmetiska operatorer endast om en given typ anpassar sig för att definiera dessa operatorer via operatoröverlagring.

Jämförelseåtgärder

Jämförelseoperatorerna, till exempel -eq, -ltoch -gt, kan jämföra operander av olika typer. Beteendet för icke-strängar och icke-primitiva typer beror på om LHS-typen implementerar gränssnitt som IEquatable och IComparable.

De samlingsbaserade jämförelseoperatorerna (-in och -contains) utför jämförelser per element -eq tills en matchning hittas. Det är varje enskilt element i samlingens operande som driver alla typer av tvång.

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

Båda exemplen returnerar true eftersom 'true' -eq $true ger $true.

Mer information finns i about_Comparison_Operators.