about_Classes

Deskripsi singkat

Menjelaskan bagaimana Anda dapat menggunakan kelas untuk membuat jenis kustom Anda sendiri.

Deskripsi panjang

PowerShell 5.0 menambahkan sintaks formal untuk menentukan kelas dan jenis lain yang ditentukan pengguna. Penambahan kelas memungkinkan pengembang dan profesional TI untuk merangkul PowerShell untuk berbagai kasus penggunaan. Ini menyederhanakan pengembangan artefak PowerShell dan mempercepat cakupan permukaan manajemen.

Deklarasi kelas adalah cetak biru yang digunakan untuk membuat instans objek pada durasi. Saat Anda menentukan kelas, nama kelas adalah nama jenisnya. Misalnya, jika Anda mendeklarasikan kelas bernama Perangkat dan menginisialisasi variabel $dev ke instans Perangkat baru, $dev adalah objek atau instans jenis Perangkat. Setiap instans Perangkat dapat memiliki nilai yang berbeda dalam propertinya.

Skenario yang didukung

  • Tentukan jenis kustom di PowerShell menggunakan semantik pemrograman berorientasi objek yang sudah dikenal seperti kelas, properti, metode, pewarisan, dll.
  • Jenis debug menggunakan bahasa PowerShell.
  • Membuat dan menangani pengecualian menggunakan mekanisme formal.
  • Tentukan sumber daya DSC dan jenis terkaitnya dengan menggunakan bahasa PowerShell.

Sintaks

Kelas dinyatakan menggunakan sintaks berikut:

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

Kelas dibuat menggunakan salah satu sintaks berikut:

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

Catatan

Saat menggunakan sintaks, [<class-name>]::new() tanda kurung di sekitar nama kelas wajib. Tanda kurung sinyal definisi jenis untuk PowerShell.

Contoh sintaks dan penggunaan

Contoh ini menunjukkan sintaks minimum yang diperlukan untuk membuat kelas yang dapat digunakan.

class Device {
    [string]$Brand
}

$dev = [Device]::new()
$dev.Brand = "Fabrikam, Inc."
$dev
Brand
-----
Fabrikam, Inc.

Properti kelas

Properti adalah variabel yang dideklarasikan pada cakupan kelas. Properti mungkin dari jenis bawaan atau instans kelas lain. Kelas tidak memiliki batasan dalam jumlah properti yang mereka miliki.

Contoh kelas dengan properti sederhana

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

$device = [Device]::new()
$device.Brand = "Fabrikam, Inc."
$device.Model = "Fbk5040"
$device.VendorSku = "5072641000"

$device
Brand          Model   VendorSku
-----          -----   ---------
Fabrikam, Inc. Fbk5040 5072641000

Contoh jenis kompleks di properti kelas

Contoh ini mendefinisikan kelas Rak kosong menggunakan kelas Perangkat . Contohnya, mengikuti yang satu ini, menunjukkan cara menambahkan perangkat ke rak dan cara memulai dengan rak yang telah dimuat sebelumnya.

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


Metode kelas

Metode menentukan tindakan yang dapat dilakukan oleh kelas. Metode dapat mengambil parameter yang menyediakan data input. Metode dapat mengembalikan output. Data yang dikembalikan oleh metode dapat berupa jenis data yang ditentukan.

Saat menentukan metode untuk kelas, Anda mereferensikan objek kelas saat ini dengan menggunakan $this variabel otomatis. Ini memungkinkan Anda mengakses properti dan metode lain yang ditentukan di kelas saat ini.

Contoh kelas sederhana dengan properti dan metode

Memperluas kelas Rak untuk menambahkan dan menghapus perangkat ke atau darinya.

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

$device = [Device]::new()
$device.Brand = "Fabrikam, Inc."
$device.Model = "Fbk5040"
$device.VendorSku = "5072641000"

$rack.AddDevice($device, 2)

$rack
$rack.GetAvailableSlots()

Slots     : 8
Devices   : {$null, $null, Fabrikam, Inc.|Fbk5040|5072641000, $null�}
Brand     :
Model     :
VendorSku :
AssetId   :

0
1
3
4
5
6
7

Output dalam metode kelas

Metode harus memiliki jenis pengembalian yang ditentukan. Jika metode tidak mengembalikan output, maka jenis output harus [void].

Dalam metode kelas, tidak ada objek yang dikirim ke alur kecuali yang disebutkan dalam return pernyataan. Tidak ada output yang tidak disengaja ke alur dari kode.

Catatan

Ini pada dasarnya berbeda dari bagaimana fungsi PowerShell menangani output, di mana semuanya masuk ke alur.

Kesalahan yang tidak mengakhiri penulisan ke aliran kesalahan dari dalam metode kelas tidak diteruskan. Anda harus menggunakan throw untuk menampilkan kesalahan yang mengakhiri. Write-* Dengan menggunakan cmdlet, Anda masih dapat menulis ke aliran output PowerShell dari dalam metode kelas. Namun, ini harus dihindari sehingga metode memancarkan objek hanya return menggunakan pernyataan .

Output metode

Contoh ini menunjukkan tidak ada output yang tidak disengaja ke alur dari metode kelas, kecuali pada return pernyataan .

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

Konstruktor memungkinkan Anda mengatur nilai default dan memvalidasi logika objek saat membuat instans kelas. Konstruktor memiliki nama yang sama dengan kelas . Konstruktor mungkin memiliki argumen, untuk menginisialisasi anggota data objek baru.

Kelas dapat memiliki nol atau lebih konstruktor yang ditentukan. Jika tidak ada konstruktor yang ditentukan, kelas diberi konstruktor tanpa parameter default. Konstruktor ini menginisialisasi semua anggota ke nilai default mereka. Jenis objek dan string diberi nilai null. Saat Anda menentukan konstruktor, tidak ada konstruktor tanpa parameter default yang dibuat. Buat konstruktor tanpa parameter jika diperlukan.

Sintaks dasar konstruktor

Dalam contoh ini, kelas Perangkat didefinisikan dengan properti dan konstruktor. Untuk menggunakan kelas ini, pengguna diharuskan untuk memberikan nilai untuk parameter yang tercantum dalam konstruktor.

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]$device = [Device]::new(
    "Fabrikam, Inc.",
    "Fbk5040",
    "5072641000"
)

$device
Brand          Model   VendorSku
-----          -----   ---------
Fabrikam, Inc. Fbk5040 5072641000

Contoh dengan beberapa konstruktor

Dalam contoh ini, kelas Perangkat didefinisikan dengan properti, konstruktor default, dan konstruktor untuk menginisialisasi instans.

Konstruktor default mengatur merek ke Tidak Terdefinisi, dan meninggalkan model dan vendor-sku dengan nilai 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]$server = [Device]::new(
    "Fabrikam, Inc.",
    "Fbk5040",
    "5072641000"
)

$someDevice, $server
Brand          Model   VendorSku
-----          -----   ---------
Undefined
Fabrikam, Inc. Fbk5040 5072641000

Atribut tersembunyi

Atribut hidden menyembunyikan properti atau metode. Properti atau metode masih dapat diakses oleh pengguna dan tersedia di semua cakupan tempat objek tersedia. Anggota tersembunyi disembunyikan dari Get-Member cmdlet dan tidak dapat ditampilkan menggunakan penyelesaian tab atau IntelliSense di luar definisi kelas.

Untuk informasi selengkapnya, lihat About_hidden.

Contoh menggunakan atribut tersembunyi

Saat objek Rak dibuat, jumlah slot untuk perangkat adalah nilai tetap yang tidak boleh diubah kapan saja. Nilai ini diketahui pada waktu pembuatan.

Menggunakan atribut tersembunyi memungkinkan pengembang untuk menjaga jumlah slot tetap tersembunyi dan mencegah perubahan yang tidak disengaja pada ukuran rak.

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("Fabrikam, Inc.", "Fbk5040", 16)

$r1
$r1.Devices.Length
$r1.Slots
Devices                       Brand          Model
-------                       -----          -----
{$null, $null, $null, $null�} Fabrikam, Inc. Fbk5040
16
16

Perhatikan properti Slot tidak ditampilkan dalam $r1 output. Namun, ukurannya diubah oleh konstruktor.

Atribut statis

Atribut static mendefinisikan properti atau metode yang ada di kelas dan tidak memerlukan instans.

Properti statis selalu tersedia, terlepas dari instansiasi kelas. Properti statis dibagikan di semua instans kelas. Metode statis selalu tersedia. Semua properti statis hidup untuk seluruh rentang sesi.

Contoh menggunakan atribut dan metode statis

Asumsikan rak yang dibuat di sini ada di pusat data Anda. Jadi, Anda ingin melacak rak dalam kode Anda.

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

Ada properti dan metode statis pengujian

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

Perhatikan bahwa jumlah rak meningkat setiap kali Anda menjalankan contoh ini.

Atribut validasi properti

Atribut validasi memungkinkan Anda menguji nilai yang diberikan ke properti memenuhi persyaratan yang ditentukan. Validasi dipicu saat nilai ditetapkan. Lihat about_functions_advanced_parameters.

Contoh menggunakan atribut validasi

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

Pewarisan di kelas PowerShell

Anda dapat memperluas kelas dengan membuat kelas baru yang berasal dari kelas yang sudah ada. Kelas turunan mewarisi properti kelas dasar. Anda dapat menambahkan atau mengambil alih metode dan properti sesuai kebutuhan.

PowerShell tidak mendukung beberapa pewarisan. Kelas tidak dapat mewarisi lebih dari satu kelas. Namun, Anda dapat menggunakan antarmuka untuk tujuan tersebut.

Implementasi pewarisan didefinisikan oleh : operator; yang berarti untuk memperluas kelas ini atau mengimplementasikan antarmuka ini. Kelas turunan harus selalu tertinggal dalam deklarasi kelas.

Contoh menggunakan sintaks pewarisan sederhana

Contoh ini menunjukkan sintaks pewarisan kelas PowerShell sederhana.

Class Derived : Base {...}

Contoh ini menunjukkan pewarisan dengan deklarasi antarmuka yang datang setelah kelas dasar.

Class Derived : Base, Interface {...}

Contoh pewarisan sederhana di kelas PowerShell

Dalam contoh ini kelas Rak dan Perangkat yang digunakan dalam contoh sebelumnya lebih didefinisikan untuk: menghindari pengulangan properti, menyelaraskan properti umum dengan lebih baik, dan menggunakan kembali logika bisnis umum.

Sebagian besar objek di pusat data adalah aset perusahaan, yang masuk akal untuk mulai melacaknya sebagai aset. Jenis perangkat ditentukan oleh DeviceType enumerasi, lihat about_Enum untuk detail tentang enumerasi.

Dalam contoh kami, kami hanya mendefinisikan Rack dan ComputeServer; kedua ekstensi ke Device kelas .

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

Memanggil konstruktor kelas dasar

Untuk memanggil konstruktor kelas dasar dari subkelas, tambahkan base kata kunci .

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

Memanggil metode kelas dasar

Untuk mengambil alih metode yang ada dalam subkelas, nyatakan metode menggunakan nama dan tanda tangan yang sama.

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

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

2

Untuk memanggil metode kelas dasar dari implementasi yang ditimpa, transmisikan ke kelas dasar ([baseclass]$this) pada pemanggilan.

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

Mewarisi dari antarmuka

Kelas PowerShell dapat mengimplementasikan antarmuka menggunakan sintaks warisan yang sama yang digunakan untuk memperluas kelas dasar. Karena antarmuka memungkinkan beberapa pewarisan, kelas PowerShell yang mengimplementasikan antarmuka dapat mewarisi dari beberapa jenis, dengan memisahkan nama jenis setelah titik dua (:) dengan koma (,). Kelas PowerShell yang mengimplementasikan antarmuka harus mengimplementasikan semua anggota antarmuka tersebut. Menghilangkan anggota antarmuka implementasi menyebabkan kesalahan penguraian waktu dalam skrip.

Catatan

PowerShell saat ini tidak mendukung deklarasi antarmuka baru dalam skrip PowerShell.

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

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

Mengimpor kelas dari modul PowerShell

Import-Module#requires dan pernyataan hanya mengimpor fungsi modul, alias, dan variabel, seperti yang didefinisikan oleh modul. Kelas tidak diimpor. Pernyataan using module mengimpor kelas yang ditentukan dalam modul. Jika modul tidak dimuat dalam sesi saat ini, using pernyataan gagal. Untuk informasi selengkapnya tentang pernyataan tersebut using , lihat about_Using.

Pernyataan mengimpor using module kelas dari modul akar (ModuleToProcess) modul skrip atau modul biner. Ini tidak secara konsisten mengimpor kelas yang ditentukan dalam modul atau kelas berlapis yang ditentukan dalam skrip yang bersumber titik ke dalam modul. Kelas yang ingin Anda sediakan untuk pengguna di luar modul harus ditentukan dalam modul akar.

Memuat kode yang baru diubah selama pengembangan

Selama pengembangan modul skrip, adalah umum untuk membuat perubahan pada kode lalu memuat versi baru modul menggunakan Import-Module dengan parameter Force . Ini hanya berfungsi untuk perubahan pada fungsi dalam modul akar. Import-Module tidak memuat ulang modul berlapis apa pun. Selain itu, tidak ada cara untuk memuat kelas yang diperbarui.

Untuk memastikan bahwa Anda menjalankan versi terbaru, Anda harus membongkar modul menggunakan Remove-Module cmdlet . Remove-Module menghapus modul akar, semua modul berlapis, dan kelas apa pun yang ditentukan dalam modul. Kemudian Anda dapat memuat ulang modul dan kelas menggunakan Import-Module dan using module pernyataan .

Praktik pengembangan umum lainnya adalah memisahkan kode Anda ke dalam file yang berbeda. Jika Anda memiliki fungsi dalam satu file yang menggunakan kelas yang ditentukan dalam modul lain, Anda harus menggunakan using module pernyataan untuk memastikan bahwa fungsi memiliki definisi kelas yang diperlukan.

Jenis PSReference tidak didukung dengan anggota kelas

[ref] Menggunakan type-cast dengan anggota kelas secara diam-diam gagal. API yang menggunakan [ref] parameter tidak dapat digunakan dengan anggota kelas. Kelas PSReference dirancang untuk mendukung objek COM. Objek COM memiliki kasus di mana Anda perlu meneruskan nilai berdasarkan referensi.

Untuk informasi selengkapnya tentang jenisnya [ref] , lihat Kelas PSReference.

Lihat juga