Láthattuk, hogy ha nem adunk meg konkrét típust, akkor a PowerShell a változónak adott érték alapján önállóan határozza meg, hogy milyen típusú változót is hoztunk létre. A parancssorba beirkált néhány utasítás esetén ez teljesen rendben is van, a (látszólagos) típustalanság rendszerint nem okoz különösebb problémát. Más a helyzet azonban hosszabb, bonyolultabb szkriptek esetén. Ekkor a kódolást, de még inkább a hibakeresést nagymértékben megkönnyíti, ha biztosak lehetünk abban, hogy milyen típusú változókat is használunk. A változó típusának meghatározása a következőképpen történik:
[1] PS C:\> [int] $szám = 1
[2] PS C:\> $szám = "blabla"
Cannot convert value "blabla" to type
"System.Int32". Error: "Input strin
g was not in a correct format."
At line:1 char:6
+ $szám <<<< = "blabla"
+
CategoryInfo : MetadataError:
(:) [], ArgumentTransformat
ionMetadataException
+
FullyQualifiedErrorId : RuntimeException
A $szam változót Int típusként hoztam létre, ezután csak számot (vagy számként értelmezhető karakterláncot) adhatunk neki értékként, más típusú változók, vagy objektumreferenciák már nem kerülhetnek bele. Az alábbi értékadás „típustalan” esetben egy karakterláncot eredményezne, de így a karakterlánc szám megfelelője lesz a változó értéke.
[3] PS C:\> [int] $szám = "123"
[4] PS C:\> $szám
123
A PowerShell változóinak típusaként tehát a .NET valamennyi érték-, illetve referenciatípusát megadhatjuk. Most tegyük egy változóba a Get-ChildItem cmdlet kimenetét:
PS C:\_munka> $lista = Get-ChildItem
Tudjuk, hogy a Get-ChildItem kimenete objektumokból áll, de vajon a változóba ez milyen formában kerül? Lehet, hogy csak a képernyőre kiírt lista van benne egyszerű szövegként? Hogy biztosak lehessünk a dologban, hívjuk segítségül a rendszergazda legjobb barátját, a get-member cmdletet:
PS C:\_munka> $lista | Get-Member
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Mode CodeProperty System.String Mode{get=Mode;}
Create Method System.Void Create(System.Security...
CreateObjRef Method System.Runtime.Remoting.ObjRef Cre...
CreateSubdirectory Method System.IO.DirectoryInfo CreateSubd...
…
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Mode CodeProperty System.String Mode{get=Mode;}
AppendText Method System.IO.StreamWriter AppendText()
CopyTo Method System.IO.FileInfo CopyTo(string d...
Create Method System.IO.FileStream Create()
CreateObjRef Method System.Runtime.Remoting.ObjRef Cre...
CreateText Method System.IO.StreamWriter CreateText()
Decrypt Method System.Void Decrypt()
…
Láthatjuk, hogy a változó teljesen az eredeti formában, System.IO.DirectoryInfo és System.IO.FileInfo objektumok alakjában tartalmazza a cmdlet kimenetét, még az sem okozott problémát, hogy a gyűjtemény két különböző típusba sorolható objektumokat tartalmazott.
Interaktív üzemmódban a Get-Member tökéletesen alkalmas a típusok felderítésére, de a típus meghatározására nem csak itt, hanem szkriptek kódjában is szükség lehet. Ebben az esetben több megoldás közül is választhatunk, talán a legegyszerűbb az -is és ‑isnot operátorok használata. Kérdezzük meg, hogy milyen objektumok alkotják a Get-Process kimenetét:
PS C:\> $a = get-process
PS C:\> $a -is "System.Diagnostics.Process"
False
Nem Process objektumok?! Persze azok, csak nem jól kérdeztünk. A Get-Process kimenete ugyanis egy gyűjtemény, a Process objektumok pedig ennek belsejében vannak. A Get-Member ezek szerint, bár teljesen érthető okok miatt, de mégis hamis eredményt ad. Hogyan tudhatjuk meg az igazi típust? A GetType() metódus az Object osztályból öröklődik, vagyis minden elképzelhető objektum és változó esetén meghívható:
PS C:\> $a = get-process
PS C:\> $a.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Sajnos a GetType() kimenetében látható oszlopok közül pont a legfontosabb nincs megjelenítve, így én általában nem simán a GetType()-ot szoktam használni, hanem rögtön megyek tovább, és a FullName tulajdonságra kérdezek rá:
PS C:\> $a.GetType().fullname
System.Object[]
A kimenet tehát egyszerűen egy tömb (a típus végén ott a ’tömbséget’ szimbolizáló []), amelynek elemei System.Object típusú objektumok, vagyis bármi beletehető. Ha beleindexelünk a tömbbe, akkor kivehetjük belőle az egyik Process objektumot, hogy annak típusát is lekérdezhessük (a Get-Member ezt előzékenyen megteszi helyettünk):
PS C:\> $a = get-process
PS C:\> $a[0] -is "System.Diagnostics.Process"
True
Mit kell tennünk akkor, ha korlátozni szeretnénk ugyan a változóba kerülő típusok körét, de olyan módon, hogy mégis több különböző (bár hasonló) típus is beleférjen? A megoldást az osztályok közötti öröklődés környékén kell keresnünk. Minden objektum ugyanis a saját konkrét típusán kívül valamennyi ősosztály típusába is beletartozik, mindent „tud” amit az ősei, de ezen felül van még néhány speciális tulajdonsága és képessége is. Egy FileInfo osztályú objektum tehát nemcsak FileInfo, hanem FileSystemInfo (az ősosztály) és Object (mindenki ősosztálya) is egyben. A változó típusát tehát úgy kell meghatároznunk, hogy az a tárolni kívánt objektumok közös őse legyen. Ha ez csak az Object osztály, akkor nincs mód korlátozásra, a klubnak mindenki tagja lehet. Az alábbi változóba például csak és kizárólag DirectoryInfo objektumot tehetünk:
PS C:\> [System.IO.DirectoryInfo]$mappa = Get-Item c:\windows
PS C:\> $mappa
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2007.07.25. 8:06 windows
Ha például FileInfo-val próbálkozunk (aki pedig elég közeli rokonJ), csak hibaüzenetet kaphatunk:
PS C:\> $mappa = Get-Item c:\windows\notepad.exe
Cannot convert the "C:\windows\notepad.exe"
value of type "System.IO.FileInfo"
to type
"System.IO.DirectoryInfo".
At line:1 char:7
+ $mappa <<<< = Get-Item c:\windows\notepad.exe
+
CategoryInfo : MetadataError:
(:) [], ArgumentTransformationMe
tadataException
+
FullyQualifiedErrorId : RuntimeException
Ha mindkét típust tárolni szeretnénk (de semmi mást!), akkor közös őstípusú változót kell létrehoznunk:
PS C:\> [System.IO.FileSystemInfo]$mappa = Get-Item c:\windows
PS C:\> [System.IO.FileSystemInfo]$mappa = Get-Item c:\windows\notepad.exe
Honnan tudhatjuk meg, hogy egy adott osztálynak mi az őse? Természetesen a .NET SDK dokumentációjából bármikor, de szerencsére nem kell feltétlenül ilyen messzire mennünk. Az ősosztály egyszerűen a PowerShellből is lekérdezhető a következő módon (BaseType oszlop):
PS C:\> [System.IO.FileInfo]
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSystemInfo
Vagy egy objektum megragadása után annak PSObject rejtett tulajdonságának, vagy más szóval nézetének TypeName tulajdonságával:
PS C:\> (Get-Item c:\windows\notepad.exe).psobject.typenames
System.IO.FileInfo
System.IO.FileSystemInfo
System.MarshalByRefObject
System.Object
Vagy akár a PSTypeNames rejtett tulajdonságot is használhatjuk:
PS C:\> (Get-Item c:\windows\notepad.exe).PSTypeNames
System.IO.FileInfo
System.IO.FileSystemInfo
System.MarshalByRefObject
System.Object
Akármelyik módszert is választottunk egy tömböt kaptunk, amiben az elemeket alulról fölfele kell értelmezni olyan módon, hogy a legősibb osztály a legalsó, azaz a System.Object, ennek leszármazottja a System.MarshalByRefObject és így tovább. A PSObject nézetről kicsit bővebben a 1.9.5 Egyedi objektumok létrehozása fejezetben lesz szó részletesebben.
Megjegyzés
Láthattuk, hogy az objektumoknak általában vannak rejtett tulajdonságaik, amelyeket alaphelyzetben a Get-Member nem láttat. Ha mégis meg akarjuk nézni ezeket, akkor a Get-Member mellett a –Force kapcsolót kell használnunk:
PS C:\> "szöveg" | Get-Member -MemberType Properties, MemberSet
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Length Property int Length {get;}
PS C:\> "szöveg" | Get-Member -MemberType Properties, MemberSet -Force
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
pstypenames CodeProperty System.Collections.ObjectModel.Collection`1[[Syste...
psadapted MemberSet psadapted {Chars, Length, Equals, get_Chars, CopyT...
psbase MemberSet psbase {Chars, Length, Equals, get_Chars, CopyTo, ...
psextended MemberSet psextended {}
psobject MemberSet psobject {BaseObject, Members, Properties, Methods...
Length Property int Length {get;}
Látható, hogy ezzel a kapcsolóval láthatóvá váltak a PowerShell által kezelt rejtett tulajdonságok.