Típusok, típuskezelés

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.



Word To HTML Converter