about_Classes
簡短描述
描述如何使用類別來建立您自己的自訂類型。
完整描述
PowerShell 5.0 新增正式語法,以定義類別和其他使用者定義型別。 新增類別可讓開發人員和 IT 專業人員採用 PowerShell,以取得更廣泛的使用案例。 它可簡化 PowerShell 成品的開發,並加速管理介面的涵蓋範圍。
類別宣告是用來在執行時間建立物件實例的藍圖。 當您定義類別時,類別名稱是型別的名稱。 例如,如果您宣告名為 Device 的類別,並將變數 $dev
初始化為 Device的新實例, $dev
則為 Device類型的物件或實例。 裝置的每個實例在其屬性中可以有不同的值。
支援的案例
- 使用熟悉的物件導向程式設計語意,在 PowerShell 中定義自訂類型,例如類別、屬性、方法、繼承等。
- 使用 PowerShell 語言對類型進行偵錯。
- 使用正式機制產生和處理例外狀況。
- 使用 PowerShell 語言定義 DSC 資源及其相關聯的類型。
語法
類別是使用下列語法來宣告:
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 = "Fabrikam, Inc."
$dev
Brand
-----
Fabrikam, Inc.
類別屬性
屬性是在類別範圍宣告的變數。 屬性可以是任何內建類型或另一個類別的實例。 類別在屬性數目上沒有限制。
具有簡單屬性的範例類別
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
類別屬性中的複雜類型範例
此範例會使用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()
$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
類別方法中的輸出
方法應該已定義傳回型別。 如果方法未傳回輸出,則輸出類型應該是 [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 值。 當您定義建構函式時,不會建立預設無參數建構函式。 如有需要,請建立無參數建構函式。
建構函式基本語法
在此範例中,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]$device = [Device]::new(
"Fabrikam, Inc.",
"Fbk5040",
"5072641000"
)
$device
Brand Model VendorSku
----- ----- ---------
Fabrikam, Inc. Fbk5040 5072641000
具有多個建構函式的範例
在此範例中, Device 類別是使用屬性、預設建構函式和初始化實例的建構函式來定義。
預設建構函式會將 品牌 設定為 Undefined,並將 model 和 vendor-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]$server = [Device]::new(
"Fabrikam, Inc.",
"Fbk5040",
"5072641000"
)
$someDevice, $server
Brand Model VendorSku
----- ----- ---------
Undefined
Fabrikam, Inc. Fbk5040 5072641000
Hidden 關鍵字
關鍵字 hidden
會隱藏屬性或方法。 使用者仍然可以存取 屬性或方法,而且可在物件可供使用的所有範圍內使用。 隱藏成員會從 Get-Member
Cmdlet 隱藏,而且無法在類別定義外部使用索引標籤完成或 IntelliSense 來顯示。
如需詳細資訊,請參閱 about_Hidden。
使用隱藏關鍵字的範例
建立 Rack 物件時,裝置的插槽數目是固定值,隨時不應該變更。 此值在建立時是已知的。
使用 hidden 關鍵字可讓開發人員隱藏位置數目,並防止不小心變更機架的大小。
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
請注意 ,輸出 中不會顯示 $r1
Slots 屬性。 不過,建構函式已變更大小。
Static 關鍵字
關鍵字 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
請注意,每次執行此範例時,機架數目都會增加。
使用屬性屬性
PowerShell 包含數個屬性類別,可用來增強資料類型資訊,並驗證指派給屬性的資料。 驗證屬性可讓您測試提供給屬性的值是否符合定義的需求。 驗證會在指派值時觸發。
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 isn't 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
如需可用屬性的詳細資訊,請參閱 about_Functions_Advanced_Parameters。
PowerShell 類別中的繼承
您可以建立衍生自現有類別的新類別來擴充類別。 衍生類別會繼承基類的屬性。 您可以視需要新增或覆寫方法和屬性。
PowerShell 不支援多重繼承。 類別無法繼承自多個類別。 不過,您可以針對該用途使用介面。
繼承實作是使用 :
語法來定義,以擴充類別或實作介面。 衍生類別應該一律在類別宣告中最左邊。
此範例顯示基本的 PowerShell 類別繼承語法。
Class Derived : Base {...}
此範例顯示基類之後的介面宣告繼承。
Class Derived : Base, Interface {...}
PowerShell 類別中的繼承範例
在此範例中,先前範例中使用的 Rack 和 Device 類別會更妥善地定義為:避免屬性重複、更妥善地對齊通用屬性,以及重複使用常見的商務邏輯。
資料中心的大部分物件都是公司資產,這很適合開始將其追蹤為資產。 列舉 DeviceType
會定義 類別所使用的裝置類型。 如需列舉的詳細資訊,請參閱 about_Enum。
enum DeviceType {
Undefined = 0
Compute = 1
Storage = 2
Networking = 4
Communications = 8
Power = 16
Rack = 32
}
在我們的範例中,我們會將 和 ComputeServer
定義為 Rack
類別的 Device
延伸模組。
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。
語句會 using module
從腳本模組或二進位模組的根模組 (ModuleToProcess
) 匯入類別。 它不會一致地匯入巢狀模組中定義的類別,或定義在模組中以點來源撰寫的腳本中定義的類別。 您想要供模組外部使用者使用的類別應該定義在根模組中。
在開發期間載入新變更的程式碼
在腳本模組的開發期間,通常會變更程式碼,然後使用Force參數載入新版本的模組 Import-Module
。 這僅適用于根模組中的函式變更。 Import-Module
不會重載任何巢狀模組。 此外,也無法載入任何更新的類別。
若要確保您執行的是最新版本,您必須使用 Remove-Module
Cmdlet 卸載模組。 Remove-Module
會移除根模組、所有巢狀模組,以及模組中定義的任何類別。 然後,您可以使用 和 using module
語句來重載模組和類別 Import-Module
。
另一個常見的開發做法是將您的程式碼分成不同的檔案。 如果您在一個檔案中使用另一個模組中定義的類別,您應該使用 using module
語句來確保函式具有所需的類別定義。
類別成員不支援 PSReference 類型
PSReference類別的類型 [ref]
加速器是簡短的。 使用 [ref]
類型轉換類別成員會以無訊息方式失敗。 使用 [ref]
參數的 API 無法與類別成員搭配使用。 PSReference類別是設計來支援 COM 物件。 COM 物件有需要以傳址方式傳入值的情況。
如需詳細資訊,請參閱 PSReference 類別。