共用方式為


關於類別

簡短描述

描述如何使用類別來建立自己的自定義類型。

完整描述

PowerShell 5.0 新增了正式語法,以定義類別和其他使用者定義型別。 新增類別可讓開發人員和IT專業人員針對更廣泛的使用案例採用PowerShell。 其可簡化PowerShell成品的開發,並加速管理介面的涵蓋範圍。

類別宣告是用來在運行時間建立物件實例的藍圖。 當您定義類別時,類別名稱是型別的名稱。 例如,如果您宣告名為 Device 的類別,並將變數 $dev 初始化為 Device 的新實例, $dev 則為 Device 類型的物件或實例。 Device 的每個實例在其屬性中可以有不同的值。

支援的案例

  • 使用熟悉的對象導向程序設計語意,在PowerShell中定義自定義類型,例如類別、屬性、方法、繼承等。
  • 使用 PowerShell 語言偵錯類型。
  • 使用正式機制產生和處理例外狀況。
  • 使用 PowerShell 語言定義 DSC 資源及其相關聯的類型。

Syntax

類別是使用下列語法來宣告:

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

類別是使用下列其中一種語法具現化:

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

注意

使用 [<class-name>]::new() 語法時,必須以括弧括住類別名稱。 括號會發出PowerShell的類型定義訊號。

範例語法和使用方式

此範例顯示建立可用類別所需的最小語法。

class Device {
    [string]$Brand
}

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

類別屬性

屬性是在類別範圍宣告的變數。 屬性可以是任何內建類型或另一個類別的實例。 類別在屬性數目上沒有任何限制。

具有簡單屬性的範例類別

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

類別屬性中的複雜類型範例

此範例會使用 Device 類別定義空的 Rack 類別。 遵循此範例示範如何將裝置新增至機架,以及如何從預先載入的機架開始。

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


類別方法

方法會定義類別可以執行的動作。 方法可能會採用提供輸入數據的參數。 方法可以傳回輸出。 方法傳回的數據可以是任何已定義的數據類型。

定義類別的方法時,您可以使用自動變數來參考目前的類別物件 $this 。 這可讓您存取目前類別中定義的屬性和其他方法。

具有屬性和方法的簡單類別範例

擴充 Rack 類別,以新增和移除裝置至該類別或從中移除裝置。

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

類別方法中的輸出

方法應該已定義傳回型別。 如果方法未傳回輸出,則輸出類型應該是 [void]

在類別方法中,除了語句中所述 return 的物件之外,不會將對象傳送至管線。 程序代碼不會意外輸出至管線。

注意

這基本上與 PowerShell 函式處理輸出的方式不同,所有項目都會移至管線。

從類別方法內部寫入錯誤數據流的非終止錯誤不會通過。 您必須使用 throw 來呈現終止錯誤。 Write-*使用 Cmdlet,您仍然可以從類別方法內寫入 PowerShell 的輸出數據流。 不過,應該避免這種情況,讓方法只 return 使用 語句發出物件。

方法輸出

此範例示範從類別方法不小心輸出至管線,但語句除外 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

建構函式

建構函式可讓您設定預設值,並在建立 類別的實例時驗證對象邏輯。 建構函式的名稱與類別相同。 建構函式可能有自變數,以初始化新對象的數據成員。

類別可以定義零個或多個建構函式。 如果未定義建構函式,則會指定類別的預設無參數建構函式。 此建構函式會將所有成員初始化為其預設值。 物件類型和字串會指定 Null 值。 當您定義建構函式時,不會建立預設無參數建構函式。 如有需要,Create 無參數建構函式。

建構函式基本語法

在此範例中,Device 類別是使用屬性和建構函式來定義。 若要使用這個類別,用戶必須提供建構函式中所列參數的值。

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

使用多個建構函式的範例

在此範例中, Device 類別是使用屬性、預設建構函式,以及初始化實例的建構函式來定義。

默認建構函式會將 品牌 設定為 Undefined,並將 模型廠商 SKU 保留為 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

隱藏屬性

屬性 hidden 會隱藏屬性或方法。 使用者仍然可以存取屬性或方法,而且可在物件可供使用的所有範圍內使用。 隱藏的成員會從 Get-Member Cmdlet 隱藏,而且無法在類別定義外部使用 Tab 鍵自動完成或 IntelliSense 來顯示。

如需詳細資訊,請參閱 About_hidden

使用隱藏屬性的範例

建立 Rack 物件時,裝置的插槽數目是固定值,不應隨時變更。 此值在建立時已知。

使用隱藏屬性可讓開發人員隱藏位置數目,並防止不小心變更機架的大小。

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

請注意,輸出中不會顯示 $r1Slots 屬性。 不過,建構函式已變更大小。

靜態屬性

屬性 static 會定義 存在於 類別中的屬性或方法,而且不需要實例。

靜態屬性一律可供使用,與類別具現化無關。 靜態屬性會跨 類別的所有實例共用。 靜態方法一律可供使用。 整個工作階段範圍的所有靜態屬性都是即時的。

使用靜態屬性和方法的範例

假設此處具現化的機架存在於數據中心。 因此,您想要追蹤程序代碼中的機架。

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

測試靜態屬性和方法存在

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

請注意,每次執行此範例時,機架數目都會增加。

屬性驗證屬性

驗證屬性可讓您測試提供給屬性的值是否符合定義的需求。 驗證會在指派值時觸發。 請參閱 about_functions_advanced_parameters

使用驗證屬性的範例

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

PowerShell 類別中的繼承

您可以建立衍生自現有類別的新類別來擴充類別。 衍生類別會繼承基類的屬性。 您可以視需要新增或覆寫方法和屬性。

PowerShell 不支援多重繼承。 類別無法繼承自多個類別。 不過,您可以針對該目的使用介面。

繼承實作是由 : 運算子所定義;這表示擴充這個類別或實作這些介面。 衍生類別應該一律在類別宣告中最左邊。

使用簡單繼承語法的範例

此範例顯示簡單的PowerShell類別繼承語法。

Class Derived : Base {...}

此範例顯示基類之後的介面宣告繼承。

Class Derived : Base, Interface {...}

PowerShell 類別中的簡單繼承範例

在此範例中,先前範例中使用的 RackDevice 類別已更妥善定義為:避免屬性重複、更妥善對齊通用屬性,以及重複使用常見的商業規則。

數據中心的大部分物件都是公司資產,這很適合用來開始將其追蹤為資產。 裝置類型是由 列舉所 DeviceType 定義,如需列舉的詳細資訊,請參閱 about_Enum

在我們的範例中,我們只定義 RackComputeServer;類別的 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

呼叫基類建構函式

若要從子類別叫用基類建構函式,請新增 base 關鍵詞。

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

叫用基類方法

若要覆寫子類別中的現有方法,請使用相同的名稱和簽章宣告方法。

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

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

2

若要從覆寫的實作呼叫基類方法,請在調用上轉換成基類 ([baseclass]$this) 。

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

繼承自介面

PowerShell 類別可以使用用來擴充基類的相同繼承語法來實作介面。 因為介面允許多重繼承,所以實作介面的 PowerShell 類別可能會繼承自多個類型,方法是在冒號 () 後面分隔類型名稱,並以逗號 , (:) 。 實作介面的PowerShell類別必須實作該介面的所有成員。 省略實作介面成員會導致腳本中的剖析時間錯誤。

注意

PowerShell 目前不支援在 PowerShell 腳本中宣告新的介面。

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

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

從 PowerShell 模組匯入類別

Import-Module#requires和語句只會匯入模組函數、別名和變數,如模組所定義。 不會匯入類別。 語句 using module 會匯入模組中定義的類別。 如果模組未載入目前會話中,語句會 using 失敗。 如需 語句的詳細資訊 using ,請參閱 about_Using

類別成員不支援 PSReference 類型

搭配類別成員使用 [ref] 型別轉換會以無訊息方式失敗。 使用 [ref] 參數的 API 無法與類別成員搭配使用。 PSReference 的設計目的是支援 COM 物件。 COM 物件有需要以傳址方式傳入值的情況。

如需類型的詳細資訊 [ref] ,請參閱 PSReference 類別

另請參閱