Udostępnij za pośrednictwem


Informacje o klasach

Krótki opis

Opisuje sposób używania klas do tworzenia własnych typów niestandardowych.

Długi opis

Program PowerShell 5.0 dodaje formalną składnię do definiowania klas i innych typów zdefiniowanych przez użytkownika. Dodanie klas umożliwia deweloperom i specjalistom IT korzystanie z programu PowerShell w szerszym zakresie przypadków użycia. Upraszcza tworzenie artefaktów programu PowerShell i przyspiesza pokrycie powierzchni zarządzania.

Deklaracja klasy to strategia używana do tworzenia wystąpień obiektów w czasie wykonywania. Podczas definiowania klasy nazwa klasy jest nazwą typu. Jeśli na przykład zadeklarujesz klasę o nazwie Urządzenie i zainicjujesz zmienną $dev do nowego wystąpienia urządzenia, $dev jest obiektem lub wystąpieniem typu Urządzenie. Każde wystąpienie urządzenia może mieć różne wartości we właściwościach.

Obsługiwane scenariusze

  • Zdefiniuj typy niestandardowe w programie PowerShell przy użyciu znanych semantyki programowania obiektowego, takich jak klasy, właściwości, metody, dziedziczenie itp.
  • Debugowanie typów przy użyciu języka programu PowerShell.
  • Generowanie i obsługa wyjątków przy użyciu mechanizmów formalnych.
  • Zdefiniuj zasoby DSC i skojarzone z nimi typy przy użyciu języka programu PowerShell.

Składnia

Klasy są deklarowane przy użyciu następującej składni:

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

Klasy są tworzone przy użyciu jednej z następujących składni:

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

Uwaga

W przypadku korzystania ze [<class-name>]::new( składni nawiasy wokół nazwy klasy są obowiązkowe. Nawiasy sygnalizujące definicję typu dla programu PowerShell.

Przykładowa składnia i użycie

W tym przykładzie przedstawiono minimalną składnię wymaganą do utworzenia klasy użytecznej.

class Device {
    [string]$Brand
}

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

Właściwości klasy

Właściwości to zmienne zadeklarowane w zakresie klasy. Właściwość może być dowolnego typu wbudowanego lub wystąpienia innej klasy. Klasy nie mają ograniczenia liczby właściwości, które mają.

Przykładowa klasa z prostymi właściwościami

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

Przykładowe typy złożone we właściwościach klasy

W tym przykładzie zdefiniowano pustą klasę Rack przy użyciu klasy Device . Poniższe przykłady pokazują, jak dodać urządzenia do stojaka i jak zacząć od wstępnie załadowanego stojaka.

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


Metody klas

Metody definiują akcje, które może wykonywać klasa. Metody mogą przyjmować parametry zapewniające dane wejściowe. Metody mogą zwracać dane wyjściowe. Dane zwracane przez metodę mogą być dowolnym zdefiniowanym typem danych.

Przykład prostej klasy z właściwościami i metodami

Rozszerzenie klasy Rack w celu dodania i usunięcia urządzeń do lub z niej.

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

Dane wyjściowe w metodach klas

Metody powinny mieć zdefiniowany typ zwracany. Jeśli metoda nie zwraca danych wyjściowych, typ danych wyjściowych powinien mieć wartość [void].

W metodach klas żadne obiekty nie są wysyłane do potoku, z wyjątkiem tych wymienionych w instrukcji return . Nie ma przypadkowych danych wyjściowych potoku z kodu.

Uwaga

Różni się to zasadniczo od sposobu obsługi danych wyjściowych przez funkcje programu PowerShell, gdzie wszystko przechodzi do potoku.

Dane wyjściowe metody

W tym przykładzie nie przedstawiono przypadkowych danych wyjściowych potoku z metod klas, z wyjątkiem instrukcji return .

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

Konstruktory umożliwiają ustawianie wartości domyślnych i weryfikowanie logiki obiektu w momencie tworzenia wystąpienia klasy. Konstruktory mają taką samą nazwę jak klasa. Konstruktory mogą mieć argumenty, aby zainicjować składowe danych nowego obiektu.

Klasa może mieć zdefiniowane zero lub więcej konstruktorów. Jeśli żaden konstruktor nie jest zdefiniowany, klasa otrzymuje domyślny konstruktor bez parametrów. Ten konstruktor inicjuje wszystkie elementy członkowskie do ich wartości domyślnych. Typy obiektów i ciągi mają wartości null. Podczas definiowania konstruktora nie jest tworzony domyślny konstruktor bez parametrów. Twórca konstruktora bez parametrów, jeśli jest potrzebny.

Składnia podstawowa konstruktora

W tym przykładzie klasa Device jest definiowana z właściwościami i konstruktorem. Aby użyć tej klasy, użytkownik musi podać wartości parametrów wymienionych w konstruktorze.

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

Przykład z wieloma konstruktorami

W tym przykładzie klasa Device jest definiowana z właściwościami, konstruktorem domyślnym i konstruktorem do inicjowania wystąpienia.

Domyślny konstruktor ustawia markę na wartość Niezdefiniowane i pozostawia model i jednostkę SKU dostawcy z wartościami null.

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

Atrybut ukryty

Atrybut hidden sprawia, że właściwość lub metoda jest mniej widoczna. Właściwość lub metoda jest nadal dostępna dla użytkownika i jest dostępna we wszystkich zakresach, w których obiekt jest dostępny. Ukryte elementy członkowskie są ukryte w poleceniu Get-Member cmdlet i nie można ich wyświetlić przy użyciu uzupełniania karty ani funkcji IntelliSense poza definicją klasy.

Przykład użycia ukrytych atrybutów

Po utworzeniu obiektu rack liczba miejsc dla urządzeń jest stałą wartością, która nie powinna być zmieniana w żadnym momencie. Ta wartość jest znana w czasie tworzenia.

Użycie ukrytego atrybutu umożliwia deweloperowi przechowywanie ukrytej liczby miejsc i uniemożliwia niezamierzone zmiany rozmiaru stojaka.

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

Zwróć uwagę, że właściwość Sloty nie jest wyświetlana w $r1 danych wyjściowych. Jednak rozmiar został zmieniony przez konstruktora.

Atrybut statyczny

Atrybut static definiuje właściwość lub metodę, która istnieje w klasie i nie wymaga wystąpienia.

Właściwość statyczna jest zawsze dostępna, niezależnie od wystąpienia klasy. Właściwość statyczna jest współużytkowana we wszystkich wystąpieniach klasy. Metoda statyczna jest zawsze dostępna. Wszystkie właściwości statyczne są aktywne dla całego zakresu sesji.

Przykład użycia atrybutów statycznych i metod

Załóżmy, że stojaki utworzone w tym miejscu istnieją w centrum danych. Dlatego chcesz śledzić stojaki w kodzie.

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

Testowanie właściwości statycznej i metody istnieje

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

Zwróć uwagę, że liczba stojaków zwiększa się za każdym razem, gdy uruchamiasz ten przykład.

Atrybuty weryfikacji właściwości

Atrybuty weryfikacji umożliwiają przetestowanie wartości podanych dla właściwości spełniających zdefiniowane wymagania. Walidacja jest wyzwalana po przypisaniu wartości. Zobacz about_functions_advanced_parameters.

Przykład użycia atrybutów weryfikacji

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

Dziedziczenie w klasach programu PowerShell

Klasę można rozszerzyć, tworząc nową klasę pochodzącą z istniejącej klasy. Klasa pochodna dziedziczy właściwości klasy bazowej. Metody i właściwości można dodawać lub zastępować zgodnie z wymaganiami.

Program PowerShell nie obsługuje wielu dziedziczenia. Klasy nie mogą dziedziczyć z więcej niż jednej klasy. W tym celu można jednak używać interfejsów.

Implementacja dziedziczenia jest definiowana przez : operatora, co oznacza rozszerzenie tej klasy lub zaimplementowanie tych interfejsów. Klasa pochodna powinna zawsze znajdować się w lewej części deklaracji klasy.

Przykład użycia prostej składni dziedziczenia

W tym przykładzie przedstawiono prostą składnię dziedziczenia klas programu PowerShell.

Class Derived : Base {...}

W tym przykładzie przedstawiono dziedziczenie z deklaracją interfejsu przychodzącą po klasie bazowej.

Class Derived : Base.Interface {...}

Przykład prostego dziedziczenia w klasach programu PowerShell

W tym przykładzie klasy Rack i Device używane w poprzednich przykładach są lepiej zdefiniowane, aby: uniknąć powtórzeń właściwości, lepiej dopasować typowe właściwości i ponownie użyć typowej logiki biznesowej.

Większość obiektów w centrum danych to zasoby firmy, co ma sens, aby rozpocząć śledzenie ich jako zasobów. Typy urządzeń są definiowane przez wyliczenie DeviceType , zobacz about_Enum , aby uzyskać szczegółowe informacje na temat wyliczenia.

W naszym przykładzie definiujemy Rack tylko rozszerzenia i ComputeServer; oba rozszerzenia Device klasy.

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

Wywoływanie konstruktorów klasy bazowej

Aby wywołać konstruktor klasy bazowej z podklasy, dodaj base słowo kluczowe.

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

Wywoływanie metod klasy bazowej

Aby zastąpić istniejące metody w podklasach, zadeklaruj metody przy użyciu tej samej nazwy i podpisu.

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

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

2

Aby wywołać metody klasy bazowej z przesłonięć implementacji, rzutuj do klasy bazowej ([baseclass]$this) przy wywołaniu.

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

Interfejsy

Składnia deklarowania interfejsów jest podobna do języka C#. Interfejsy można zadeklarować po typach podstawowych lub bezpośrednio po dwukropku (:), gdy nie określono typu podstawowego. Rozdziel wszystkie nazwy typów przecinkami.

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

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

Importowanie klas z modułu programu PowerShell

Import-Module#requires i instrukcja importuje tylko funkcje modułu, aliasy i zmienne zdefiniowane przez moduł. Klasy nie są importowane. Instrukcja using module importuje klasy zdefiniowane w module. Jeśli moduł nie zostanie załadowany w bieżącej sesji, using instrukcja zakończy się niepowodzeniem.

Zobacz też