Freigeben über


Über Klassen

Kurzbeschreibung

Beschreibt, wie Sie Klassen zum Erstellen eigener benutzerdefinierter Typen verwenden können.

Lange Beschreibung

PowerShell 5.0 fügt eine formale Syntax hinzu, um Klassen und andere benutzerdefinierte Typen zu definieren. Durch das Hinzufügen von Klassen können Entwickler und IT-Experten PowerShell für eine breitere Palette von Anwendungsfällen nutzen. Es vereinfacht die Entwicklung von PowerShell-Artefakten und beschleunigt die Abdeckung von Verwaltungsoberflächen.

Eine Klassendeklaration ist ein Blueprint zum Erstellen von Instanzen von Objekten zur Laufzeit. Wenn Sie eine Klasse definieren, ist der Klassenname der Name des Typs. Wenn Sie beispielsweise eine Klasse mit dem Namen Device deklarieren und eine Variable $dev in eine neue Instanz von Deviceinitialisieren, ist $dev ein Objekt oder eine Instanz vom Typ Device. Jede Instanz von Device kann unterschiedliche Werte in ihren Eigenschaften aufweisen.

Unterstützte Szenarien

  • Definieren Sie benutzerdefinierte Typen in PowerShell mithilfe der vertrauten objektorientierten Programmiersemantik wie Klassen, Eigenschaften, Methoden, Vererbung usw.
  • Debuggen von Typen mithilfe der PowerShell-Sprache.
  • Generieren und Behandeln von Ausnahmen mithilfe formaler Mechanismen.
  • Definieren Sie DSC-Ressourcen und die zugehörigen Typen mithilfe der PowerShell-Sprache.

Syntax

Klassen werden mit der folgenden Syntax deklariert:

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 werden mit einer der folgenden Syntaxen instanziiert:

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

Hinweis

Bei Verwendung der [<class-name>]::new(-Syntax sind Klammern um den Klassennamen obligatorisch. Die Klammern signalisieren eine Typdefinition für PowerShell.

Beispiel für Syntax und Verwendung

In diesem Beispiel wird die Mindestsyntax veranschaulicht, die zum Erstellen einer verwendbaren Klasse erforderlich ist.

class Device {
    [string]$Brand
}

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

Klasseneigenschaften

Eigenschaften sind Variablen, die auf Klassenebene deklariert sind. Eine Eigenschaft kann von einem beliebigen integrierten Typ oder einer Instanz einer anderen Klasse sein. Klassen haben keine Einschränkung in der Anzahl der Eigenschaften, die sie haben.

Beispielklasse mit einfachen Eigenschaften

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

Beispiele für komplexe Typen in Klasseneigenschaften

In diesem Beispiel wird eine leere Rack-Klasse mithilfe der Device-Klasse definiert. Die folgenden Beispiele zeigen, wie Sie dem Rack Geräte hinzufügen und wie Sie mit einem vorinstallierten Rack beginnen.

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


Klassenmethoden

Methoden definieren die Aktionen, die von einer Klasse ausgeführt werden können. Methoden können Parameter annehmen, die Eingabedaten bereitstellen. Methoden können die Ausgabe zurückgeben. Daten, die von einer Methode zurückgegeben werden, können einen beliebigen definierten Datentyp aufweisen.

Beispiel für eine einfache Klasse mit Eigenschaften und Methoden

Erweitern der Rack-Klasse , um ihr Geräte hinzuzufügen und daraus zu entfernen.

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

Ausgabe in Klassenmethoden

Für Methoden sollte ein Rückgabetyp definiert sein. Wenn eine Methode keine Ausgabe zurückgibt, sollte der Ausgabetyp lauten [void].

In Klassenmethoden werden keine Objekte an die Pipeline gesendet, außer denen, die in der return Anweisung erwähnt werden. Es gibt keine versehentliche Ausgabe für die Pipeline aus dem Code.

Hinweis

Dies unterscheidet sich grundlegend davon, wie PowerShell-Funktionen die Ausgabe verarbeiten, wo alles an die Pipeline geht.

Methodenausgabe

In diesem Beispiel wird veranschaulicht, dass keine versehentliche Ausgabe von Klassenmethoden an die Pipeline erfolgt, außer in der return Anweisung.

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

Konstruktor

Mit Konstruktoren können Sie Standardwerte festlegen und die Objektlogik zum Zeitpunkt der Erstellung der Instanz der Klasse überprüfen. Konstruktoren haben denselben Namen wie die Klasse. Konstruktoren können Argumente haben, um die Datenmember des neuen Objekts zu initialisieren.

Die Klasse kann null oder mehr Konstruktoren definiert haben. Wenn kein Konstruktor definiert ist, erhält die Klasse einen standardparameterlosen Konstruktor. Dieser Konstruktor initialisiert alle Member mit ihren Standardwerten. Objekttypen und Zeichenfolgen werden NULL-Werte angegeben. Wenn Sie den Konstruktor definieren, wird kein standardparameterloser Konstruktor erstellt. Erstellen Sie bei Bedarf einen parameterlosen Konstruktor.

Grundlegende Syntax des Konstruktors

In diesem Beispiel wird die Device-Klasse mit Eigenschaften und einem Konstruktor definiert. Um diese Klasse verwenden zu können, muss der Benutzer Werte für die im Konstruktor aufgeführten Parameter angeben.

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

Beispiel mit mehreren Konstruktoren

In diesem Beispiel wird die Device-Klasse mit Eigenschaften, einem Standardkonstruktor und einem Konstruktor zum Initialisieren der Instanz definiert.

Der Standardkonstruktor legt die Marke auf Undefined fest und belässt model und vendor-sku mit NULL-Werten.

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

Verstecktes Attribut

Das hidden Attribut macht eine Eigenschaft oder Methode weniger sichtbar. Die Eigenschaft oder Methode ist für den Benutzer weiterhin zugänglich und in allen Bereichen verfügbar, in denen das Objekt verfügbar ist. Ausgeblendete Member werden im Cmdlet ausgeblendet und können nicht mithilfe der Get-Member Tab-Vervollständigung oder IntelliSense außerhalb der Klassendefinition angezeigt werden.

Beispiel für die Verwendung ausgeblendeter Attribute

Wenn ein Rack-Objekt erstellt wird, ist die Anzahl der Steckplätze für Geräte ein fester Wert, der zu keinem Zeitpunkt geändert werden sollte. Dieser Wert ist zum Zeitpunkt der Erstellung bekannt.

Die Verwendung des versteckten Attributs ermöglicht es dem Entwickler, die Anzahl der Steckplätze verborgen zu halten und unbeabsichtigte Änderungen der Größe des Racks zu verhindern.

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

Beachten Sie, dass die Eigenschaft "Slots " in der $r1 Ausgabe nicht angezeigt wird. Die Größe wurde jedoch vom Konstruktor geändert.

Statisches Attribut

Das static Attribut definiert eine Eigenschaft oder eine Methode, die in der Klasse vorhanden ist und keine Instanz benötigt.

Eine statische Eigenschaft ist immer verfügbar, unabhängig von der Klasseninstanziierung. Eine statische Eigenschaft wird für alle Instanzen der Klasse freigegeben. Eine statische Methode ist immer verfügbar. Alle statischen Eigenschaften werden für die gesamte Sitzungsspanne live ausgeführt.

Beispiel für die Verwendung statischer Attribute und Methoden

Angenommen, die hier instanziierten Racks sind in Ihrem Rechenzentrum vorhanden. Sie möchten also den Überblick über die Racks in Ihrem Code behalten.

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 Testeigenschaft und -methode vorhanden

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

Beachten Sie, dass die Anzahl der Racks jedes Mal zunimmt, wenn Sie dieses Beispiel ausführen.

Attribute zur Eigenschaftsvalidierung

Mithilfe von Überprüfungsattributen können Sie testen, ob die werte, die eigenschaften angegeben wurden, den definierten Anforderungen entsprechen. Die Überprüfung wird ausgelöst, sobald der Wert zugewiesen ist. Siehe about_functions_advanced_parameters.

Beispiel für die Verwendung von Validierungsattributen

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

Vererbung in PowerShell-Klassen

Sie können eine Klasse erweitern, indem Sie eine neue Klasse erstellen, die von einer vorhandenen Klasse abgeleitet wird. Die abgeleitete Klasse erbt die Eigenschaften der Basisklasse. Sie können Methoden und Eigenschaften nach Bedarf hinzufügen oder überschreiben.

PowerShell unterstützt keine Mehrfachvererbung. Klassen können nicht von mehr als einer Klasse erben. Zu diesem Zweck können Sie jedoch Schnittstellen verwenden.

Die Vererbungsimplementierung wird durch den : Operator definiert, was bedeutet, dass diese Klasse erweitert oder diese Schnittstellen implementiert werden. Die abgeleitete Klasse sollte immer in der Klassendeklaration ganz links sein.

Beispiel für die Verwendung einer einfachen Vererbungssyntax

Dieses Beispiel zeigt die einfache PowerShell-Syntax für die Klassenvererbung.

Class Derived : Base {...}

Dieses Beispiel zeigt die Vererbung mit einer Schnittstellendeklaration, die nach der Basisklasse kommt.

Class Derived : Base.Interface {...}

Beispiel für eine einfache Vererbung in PowerShell-Klassen

In diesem Beispiel sind die in den vorherigen Beispielen verwendeten Klassen Rack und Device besser definiert, um Wiederholungen von Eigenschaften zu vermeiden, gemeinsame Eigenschaften besser auszurichten und allgemeine Geschäftslogik wiederzuverwenden.

Bei den meisten Objekten im Rechenzentrum handelt es sich um Unternehmensanlagen, weshalb es sinnvoll ist, sie als Anlagen zu verfolgen. Gerätetypen werden durch die DeviceType Enumeration definiert, siehe about_Enum für Details zu Enumerationen.

In unserem Beispiel definieren Rack wir nur und ComputeServer; beides Erweiterungen für die Device Klasse.

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

Aufrufen von Basisklassenkonstruktoren

Um einen Basisklassenkonstruktor aus einer Unterklasse aufzurufen, fügen Sie das base Schlüsselwort hinzu.

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

Aufrufen von Basisklassenmethoden

Um vorhandene Methoden in Unterklassen zu überschreiben, deklarieren Sie Methoden mit demselben Namen und derselben Signatur.

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

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

2

Um Basisklassenmethoden aus überschriebenen Implementierungen aufzurufen, wandeln Sie sie beim Aufruf in die Basisklasse ([baseclass]$this) um.

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

Schnittstellen

Die Syntax zum Deklarieren von Schnittstellen ähnelt der von C#. Sie können Schnittstellen nach Basistypen oder unmittelbar nach einem Doppelpunkt (:) deklarieren, wenn kein Basistyp angegeben ist. Trennen Sie alle Typnamen durch Kommas.

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

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

Importieren von Klassen aus einem PowerShell-Modul

Import-Module und die #requires-Anweisung importieren nur die Modulfunktionen, Aliase und Variablen, wie vom Modul definiert. Klassen werden nicht importiert. Die using module Anweisung importiert die im Modul definierten Klassen. Wenn das Modul in der aktuellen Sitzung nicht geladen wird, schlägt die using Anweisung fehl.

Siehe auch