關於類別
簡短描述
描述如何使用類別來建立自己的自定義類型。
完整描述
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
請注意,輸出中不會顯示 $r1
Slots 屬性。 不過,建構函式已變更大小。
靜態屬性
屬性 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 類別中的簡單繼承範例
在此範例中,先前範例中使用的 Rack 和 Device 類別已更妥善定義為:避免屬性重複、更妥善對齊通用屬性,以及重複使用常見的商業規則。
數據中心的大部分物件都是公司資產,這很適合用來開始將其追蹤為資產。 裝置類型是由 列舉所 DeviceType
定義,如需列舉的詳細資訊,請參閱 about_Enum 。
在我們的範例中,我們只定義 Rack
和 ComputeServer
;類別的 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 類別。