about_Classes

Korte beschrijving

Beschrijft hoe u klassen kunt gebruiken om uw eigen aangepaste typen te maken.

Lange beschrijving

PowerShell 5.0 voegt een formele syntaxis toe om klassen en andere door de gebruiker gedefinieerde typen te definiëren. Door de toevoeging van klassen kunnen ontwikkelaars en IT-professionals PowerShell gebruiken voor een breder scala aan gebruiksgevallen. Het vereenvoudigt de ontwikkeling van PowerShell-artefacten en versnelt de dekking van beheeroppervlakken.

Een klassedeclaratie is een blauwdruk die wordt gebruikt voor het maken van exemplaren van objecten tijdens run time. Wanneer u een klasse definieert, is de klassenaam de naam van het type. Als u bijvoorbeeld een klasse met de naam Apparaat declareert en een variabele initialiseert naar een nieuw exemplaar van $dev Apparaat, is een object of exemplaar $dev van het type Apparaat. Elk exemplaar van Apparaat kan verschillende waarden hebben in de eigenschappen.

Ondersteunde scenario's

  • Definieer aangepaste typen in PowerShell met behulp van bekende objectgeoriënteerde programmeersemantiek zoals klassen, eigenschappen, methoden, overname, enzovoort.
  • Fouten opsporen in typen met behulp van de PowerShell-taal.
  • Uitzonderingen genereren en afhandelen met behulp van formele mechanismen.
  • Definieer DSC-resources en de bijbehorende typen met behulp van de PowerShell-taal.

Syntax

Klassen worden gedeclareerd met behulp van de volgende syntaxis:

class <class-name> [: [<base-class>][,<interface-list]] {
    [[<attribute>] [hidden] [static] <property-definition> ...]
    [<class-name>([<constructor-argument-list>])
      {<constructor-statement-list>} ...]
    [[<attribute>] [hidden] [static] <method-definition> ...]
}

Klassen worden gemaakt met behulp van een van de volgende syntaxis:

[$<variable-name> =] New-Object -TypeName <class-name> [
  [-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])

Notitie

Wanneer u de [<class-name>]::new() syntaxis gebruikt, zijn haakjes rond de klassenaam verplicht. De haakjes signaleren een typedefinitie voor PowerShell.

Voorbeeld van syntaxis en gebruik

In dit voorbeeld ziet u de minimale syntaxis die nodig is om een bruikbaar klasse te maken.

class Device {
    [string]$Brand
}

$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft

Klasse-eigenschappen

Eigenschappen zijn variabelen die zijn gedeclareerd op klassebereik. Een eigenschap kan van elk ingebouwd type of een instantie van een andere klasse zijn. Klassen hebben geen beperking in het aantal eigenschappen dat ze hebben.

Voorbeeldklasse met eenvoudige eigenschappen

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

$device = [Device]::new()
$device.Brand = "Microsoft"
$device.Model = "Surface Pro 4"
$device.VendorSku = "5072641000"

$device
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Voorbeeld van complexe typen in klasse-eigenschappen

In dit voorbeeld wordt een lege Rack-klasse met behulp van de apparaatklasse definieert. De voorbeelden, na deze, laten zien hoe u apparaten toevoegt aan het rek en hoe u begint met een vooraf geladen rek.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

class Rack {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new(8)

}

$rack = [Rack]::new()

$rack

Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, $null, $null...}


Klassemethoden

Methoden definiëren de acties die een klasse kan uitvoeren. Methoden kunnen parameters gebruiken die invoergegevens bevatten. Methoden kunnen uitvoer retourneren. Gegevens die door een methode worden geretourneerd, kunnen elk gedefinieerd gegevenstype zijn.

Bij het definiëren van een methode voor een klasse verwijst u naar het huidige klasseobject met behulp van de $this automatische variabele. Hiermee hebt u toegang tot eigenschappen en andere methoden die zijn gedefinieerd in de huidige klasse.

Voorbeeld van een eenvoudige klasse met eigenschappen en methoden

De Rack-klasse uitbreiden om apparaten toe te voegen aan of te verwijderen.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    [string]ToString(){
        return ("{0}|{1}|{2}" -f $this.Brand, $this.Model, $this.VendorSku)
    }
}

class Rack {
    [int]$Slots = 8
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void]RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }

    [int[]] GetAvailableSlots(){
        [int]$i = 0
        return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
    }
}

$rack = [Rack]::new()

$surface = [Device]::new()
$surface.Brand = "Microsoft"
$surface.Model = "Surface Pro 4"
$surface.VendorSku = "5072641000"

$rack.AddDevice($surface, 2)

$rack
$rack.GetAvailableSlots()

Slots     : 8
Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, Microsoft|Surface Pro 4|5072641000, $null...}

0
1
3
4
5
6
7

Uitvoer in klassemethoden

Methoden moeten een retourtype hebben gedefinieerd. Als een methode geen uitvoer retournt, moet het uitvoertype [void] zijn.

In klassemethoden worden geen objecten verzonden naar de pijplijn, behalve de objecten die worden vermeld in de return instructie . Er is geen onbedoelde uitvoer van de code naar de pijplijn.

Notitie

Dit verschilt in principe van de manier waarop PowerShell-functies de uitvoer verwerken, waarbij alles naar de pijplijn gaat.

Niet-beëindigingsfouten die vanuit een klassemethode naar de foutstroom worden geschreven, worden niet doorgegeven. U moet gebruiken throw om een beëindigingsfout aan het oppervlak te brengen. Met behulp van de cmdlets kunt u nog steeds schrijven naar de uitvoerstromen van Write-* PowerShell vanuit een klassemethode. Dit moet echter worden vermeden, zodat met de methode alleen objecten worden gebruikt met behulp van de return -instructie.

Uitvoer van methode

In dit voorbeeld wordt geen onbedoelde uitvoer naar de pijplijn van klassemethoden gedemonstreerd, behalve op de return instructie .

class FunWithIntegers
{
    [int[]]$Integers = 0..10

    [int[]]GetOddIntegers(){
        return $this.Integers.Where({ ($_ % 2) })
    }

    [void] GetEvenIntegers(){
        # this following line doesn't go to the pipeline
        $this.Integers.Where({ ($_ % 2) -eq 0})
    }

    [string]SayHello(){
        # this following line doesn't go to the pipeline
        "Good Morning"

        # this line goes to the pipeline
        return "Hello World"
    }
}

$ints = [FunWithIntegers]::new()
$ints.GetOddIntegers()
$ints.GetEvenIntegers()
$ints.SayHello()
1
3
5
7
9
Hello World

Constructor

Met constructors kunt u standaardwaarden instellen en objectlogica valideren op het moment dat u het exemplaar van de klasse maakt. Constructors hebben dezelfde naam als de klasse . Constructors kunnen argumenten hebben om de gegevensleden van het nieuwe object te initialiseren.

Voor de klasse kunnen nul of meer constructors zijn gedefinieerd. Als er geen constructor is gedefinieerd, krijgt de klasse een standaardparameterloze constructor. Deze constructor initialiseert alle leden naar hun standaardwaarden. Objecttypen en tekenreeksen krijgen null-waarden. Wanneer u een constructor definieert, wordt er geen standaardparameterloze constructor gemaakt. Maak een parameterloze constructor als dat nodig is.

Basissyntaxis van constructor

In dit voorbeeld wordt de klasse Device gedefinieerd met eigenschappen en een constructor. Als u deze klasse wilt gebruiken, moet de gebruiker waarden opgeven voor de parameters die in de constructor worden vermeld.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$surface
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Voorbeeld met meerdere constructors

In dit voorbeeld wordt de klasse Device gedefinieerd met eigenschappen, een standaard constructor en een constructor om het exemplaar te initialiseren.

De standaard constructor stelt het merk in op Undefined en laat model en vendor-sku met null-waarden staan.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(){
        $this.Brand = 'Undefined'
    }

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$somedevice = [Device]::new()
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$somedevice
$surface
Brand       Model           VendorSku
-----       -----           ---------
Undefined
Microsoft   Surface Pro 4   5072641000

Verborgen kenmerk

Het hidden kenmerk verbergt een eigenschap of methode. De eigenschap of methode is nog steeds toegankelijk voor de gebruiker en is beschikbaar in alle bereiken waarin het object beschikbaar is. Verborgen leden worden verborgen voor de cmdlet en kunnen niet worden weergegeven met tabinvulling of Get-Member IntelliSense buiten de klassedefinitie.

Zie voor meer informatie About_hidden.

Voorbeeld van het gebruik van verborgen kenmerken

Wanneer een Rack-object wordt gemaakt, is het aantal sleuven voor apparaten een vaste waarde die op geen enkel moment mag worden gewijzigd. Deze waarde is bekend tijdens het maken.

Door het verborgen kenmerk te gebruiken, kan de ontwikkelaar het aantal sleuven verborgen houden en voorkomt u onbedoelde wijzigingen in de grootte van het rek.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    [int] hidden $Slots = 8
    [string]$Brand
    [string]$Model
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)
    }
}

[Rack]$r1 = [Rack]::new("Microsoft", "Surface Pro 4", 16)

$r1
$r1.Devices.Length
$r1.Slots
Brand     Model         Devices
-----     -----         -------
Microsoft Surface Pro 4 {$null, $null, $null, $null...}
16
16

De eigenschap Slots wordt niet weergegeven in de $r1 uitvoer. De grootte is echter gewijzigd door de constructor.

Statisch kenmerk

Het static kenmerk definieert een eigenschap of een methode die in de klasse bestaat en geen instantie nodig heeft.

Een statische eigenschap is altijd beschikbaar, onafhankelijk van klasse-instantiëring. Een statische eigenschap wordt gedeeld met alle exemplaren van de klasse. Er is altijd een statische methode beschikbaar. Alle statische eigenschappen zijn live voor de hele sessie.

Voorbeeld van het gebruik van statische kenmerken en methoden

Stel dat de racks die hier zijn gemaakt, aanwezig zijn in uw datacenter. U wilt dus de racks in uw code bijhouden.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    hidden [int] $Slots = 8
    static [Rack[]]$InstalledRacks = @()
    [string]$Brand
    [string]$Model
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.AssetId = $id
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)

        ## add rack to installed racks
        [Rack]::InstalledRacks += $this
    }

    static [void]PowerOffRacks(){
        foreach ($rack in [Rack]::InstalledRacks) {
            Write-Warning ("Turning off rack: " + ($rack.AssetId))
        }
    }
}

Statische eigenschap en methode testen

PS> [Rack]::InstalledRacks.Length
0

PS> [Rack]::PowerOffRacks()

PS> (1..10) | ForEach-Object {
>>   [Rack]::new("Adatum Corporation", "Standard-16",
>>     $_.ToString("Std0000"), 16)
>> } > $null

PS> [Rack]::InstalledRacks.Length
10

PS> [Rack]::InstalledRacks[3]
Brand              Model       AssetId Devices
-----              -----       ------- -------
Adatum Corporation Standard-16 Std0004 {$null, $null, $null, $null...}

PS> [Rack]::PowerOffRacks()
WARNING: Turning off rack: Std0001
WARNING: Turning off rack: Std0002
WARNING: Turning off rack: Std0003
WARNING: Turning off rack: Std0004
WARNING: Turning off rack: Std0005
WARNING: Turning off rack: Std0006
WARNING: Turning off rack: Std0007
WARNING: Turning off rack: Std0008
WARNING: Turning off rack: Std0009
WARNING: Turning off rack: Std0010

U ziet dat het aantal rekken toeneemt telkens wanneer u dit voorbeeld gebruikt.

Eigenschapsvalidatiekenmerken

Met validatiekenmerken kunt u testen of waarden die aan eigenschappen worden gegeven, voldoen aan gedefinieerde vereisten. Validatie wordt geactiveerd op het moment dat de waarde wordt toegewezen. Zie about_functions_advanced_parameters.

Voorbeeld van het gebruik van validatiekenmerken

class Device {
    [ValidateNotNullOrEmpty()][string]$Brand
    [ValidateNotNullOrEmpty()][string]$Model
}

[Device]$dev = [Device]::new()

Write-Output "Testing dev"
$dev

$dev.Brand = ""
Testing dev

Brand Model
----- -----

Exception setting "Brand": "The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again."
At C:\tmp\Untitled-5.ps1:11 char:1
+ $dev.Brand = ""
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

Overname in PowerShell-klassen

U kunt een klasse uitbreiden door een nieuwe klasse te maken die is afgeleid van een bestaande klasse. De afgeleide klasse neemt de eigenschappen van de basisklasse over. U kunt methoden en eigenschappen toevoegen of overschrijven, indien nodig.

PowerShell biedt geen ondersteuning voor meervoudige overname. Klassen kunnen niet van meer dan één klasse overnemen. U kunt echter wel interfaces voor dat doel gebruiken.

Overname-implementatie wordt gedefinieerd door de : operator. Dit betekent dat deze klasse wordt uitgebreid of dat deze interfaces worden geïmplementeerd. De afgeleide klasse moet altijd het meest links staan in de klassedeclaratie.

Voorbeeld van het gebruik van eenvoudige overnamesyntaxis

In dit voorbeeld ziet u de eenvoudige syntaxis voor overname van PowerShell-klassen.

Class Derived : Base {...}

In dit voorbeeld ziet u overname met een interfacedeclaratie die na de basisklasse komt.

Class Derived : Base, Interface {...}

Voorbeeld van eenvoudige overname in PowerShell-klassen

In dit voorbeeld zijn de rack- en apparaatklassen die in de vorige voorbeelden worden gebruikt beter gedefinieerd om: herhalingen van eigenschappen voorkomen, algemene eigenschappen beter uitlijnen en algemene bedrijfslogica opnieuw gebruiken.

De meeste objecten in het datacenter zijn bedrijfsmiddelen, wat zinvol is om ze als assets bij te houden. Apparaattypen worden gedefinieerd door DeviceType de -enumeratie. Zie about_Enum voor meer informatie over -enumeraties.

In ons voorbeeld definiëren we alleen en Rack ComputeServer ; beide extensies voor de klasse Device .

enum DeviceType {
    Undefined = 0
    Compute = 1
    Storage = 2
    Networking = 4
    Communications = 8
    Power = 16
    Rack = 32
}

class Asset {
    [string]$Brand
    [string]$Model
}

class Device : Asset {
    hidden [DeviceType]$devtype = [DeviceType]::Undefined
    [string]$Status

    [DeviceType] GetDeviceType(){
        return $this.devtype
    }
}

class ComputeServer : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Compute
    [string]$ProcessorIdentifier
    [string]$Hostname
}

class Rack : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Rack
    hidden [int]$Slots = 8

    [string]$Datacenter
    [string]$Location
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack (){
        ## Just create the default rack with 8 slots
    }

    Rack ([int]$s){
        ## Add argument validation logic here
        $this.Devices = [Device[]]::new($s)
    }

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void] RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }
}

$FirstRack = [Rack]::new(16)
$FirstRack.Status = "Operational"
$FirstRack.Datacenter = "PNW"
$FirstRack.Location = "F03R02.J10"

(0..15).ForEach({
    $ComputeServer = [ComputeServer]::new()
    $ComputeServer.Brand = "Fabrikam, Inc."       ## Inherited from Asset
    $ComputeServer.Model = "Fbk5040"              ## Inherited from Asset
    $ComputeServer.Status = "Installed"           ## Inherited from Device
    $ComputeServer.ProcessorIdentifier = "x64"    ## ComputeServer
    $ComputeServer.Hostname = ("r1s" + $_.ToString("000")) ## ComputeServer
    $FirstRack.AddDevice($ComputeServer, $_)
  })

$FirstRack
$FirstRack.Devices
Datacenter : PNW
Location   : F03R02.J10
Devices    : {r1s000, r1s001, r1s002, r1s003...}
Status     : Operational
Brand      :
Model      :

ProcessorIdentifier : x64
Hostname            : r1s000
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

ProcessorIdentifier : x64
Hostname            : r1s001
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

<... content truncated here for brevity ...>

ProcessorIdentifier : x64
Hostname            : r1s015
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

Basisklasse-constructors aanroepen

Als u een basisklasse constructor wilt aanroepen vanuit een subklasse, voegt u het base trefwoord toe.

class Person {
    [int]$Age

    Person([int]$a)
    {
        $this.Age = $a
    }
}

class Child : Person
{
    [string]$School

    Child([int]$a, [string]$s ) : base($a) {
        $this.School = $s
    }
}

[Child]$littleone = [Child]::new(10, "Silver Fir Elementary School")

$littleone.Age

10

Basisklassemethoden aanroepen

Als u bestaande methoden in subklassen wilt overschrijven, declareer methoden met dezelfde naam en handtekening.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
}

[ChildClass1]::new().days()

2

Als u basisklassemethoden wilt aanroepen vanuit overschrijven implementaties, cast u naar de basisklasse ([baseclass]$this) bij aanroepen.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
    [int]basedays() {return ([BaseClass]$this).days()}
}

[ChildClass1]::new().days()
[ChildClass1]::new().basedays()

2
1

Overnemen van interfaces

PowerShell-klassen kunnen een interface implementeren met dezelfde overnamesyntaxis die wordt gebruikt om basisklassen uit te breiden. Omdat interfaces meerdere overname toestaan, kan een PowerShell-klasse die een interface implementeert, overnemen van meerdere typen door de typenamen na de dubbele punt ( ) te scheiden met : komma's ( , ). Een PowerShell-klasse die een interface implementeert, moet alle leden van die interface implementeren. Het weglaten van de leden van de implementatie-interface veroorzaakt een parse-tijdfout in het script.

Notitie

PowerShell biedt momenteel geen ondersteuning voor het declareren van nieuwe interfaces in een PowerShell-script.

class MyComparable : System.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

class MyComparableBar : bar, System.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

Klassen importeren uit een PowerShell-module

Import-Module en de #requires instructie importeert alleen de modulefuncties, aliassen en variabelen, zoals gedefinieerd door de module. Klassen worden niet geïmporteerd. Met using module de instructie worden de klassen geïmporteerd die in de module zijn gedefinieerd. Als de module niet is geladen in de huidige sessie, mislukt using de instructie. Zie voor meer informatie over de using instructie about_Using.

Met using module de instructie worden klassen geïmporteerd uit de hoofdmodule ( ) van een ModuleToProcess scriptmodule of binaire module. Klassen die zijn gedefinieerd in geneste modules of klassen die zijn gedefinieerd in scripts die dot-source in de module zijn gedefinieerd, worden niet consistent geïmporteerd. Klassen die u beschikbaar wilt stellen aan gebruikers buiten de module, moeten worden gedefinieerd in de hoofdmodule.

Nieuw gewijzigde code laden tijdens de ontwikkeling

Tijdens het ontwikkelen van een scriptmodule is het gebruikelijk om wijzigingen aan te brengen in de code en vervolgens de nieuwe versie van de module te laden met behulp Import-Module van de parameter Force. Dit werkt alleen voor wijzigingen in functies in de hoofdmodule. Import-Module laadt geen geneste modules opnieuw. Er is ook geen manier om bijgewerkte klassen te laden.

Om ervoor te zorgen dat u de nieuwste versie gebruikt, moet u de module verwijderen met behulp van Remove-Module de cmdlet . Remove-Module verwijdert de hoofdmodule, alle geneste modules en eventuele klassen die in de modules zijn gedefinieerd. Vervolgens kunt u de module en de klassen opnieuw laden met en Import-Module de using module instructie .

Een andere veelvoorkomende ontwikkeling is het scheiden van uw code in verschillende bestanden. Als u een functie in het ene bestand hebt dat klassen gebruikt die zijn gedefinieerd in een andere module, moet u de instructie gebruiken om ervoor te zorgen dat de functies de benodigde using module klassedefinities hebben.

Het type PSReference wordt niet ondersteund door klasseleden

Het gebruik [ref] van de typecast met een klasselid mislukt op de stille manier. API's die gebruikmaken [ref] van parameters kunnen niet worden gebruikt met klasseleden. De PSReference-klasse is ontworpen ter ondersteuning van COM-objecten. COM-objecten hebben gevallen waarin u een waarde moet doorgeven via een verwijzing.

Zie [ref] PSReference Classvoor meer informatie over het type .

Zie ook