about_Classes_Constructors

簡短描述

描述如何定義PowerShell類別的建構函式。

詳細描述

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

PowerShell 類別建構函式會定義為 類別上的特殊方法。 它們的行為與 PowerShell 類別方法的行為相同,但有下列例外狀況:

  • 建構函式沒有輸出類型。 他們無法使用 return 關鍵詞。
  • 建構函式的名稱一律與類別相同。
  • 無法直接呼叫建構函式。 它們只會在建立實例時執行。
  • 建構函式永遠不會出現在 Cmdlet 的輸出中 Get-Member

如需 PowerShell 類別方法的詳細資訊,請參閱 about_Classes_Methods

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

您也可以定義無 參數靜態建構函式

語法

類別建構函式會使用下列語法:

默認建構函式語法

<class-name> () [: base([<params>])] {
    <body>
}

靜態建構函式語法

static <class-name> () [: base([<params>])] {
    <body>
}

參數化建構函式語法 (一行)

<class-name> ([[<parameter-type>]$<parameter-name>[, [<parameter-type>]$<parameter-name>...]]) [: base([<params>])] {
    <body>
}

參數化建構函式語法 (多行)

<class-name> (
    [<parameter-type>]$<parameter-name>[,
    [<parameter-type>]$<parameter-name>...]
) [: base([<params>])] {
    <body>
}

範例

範例 1 - 使用預設建構函式定義類別

ExampleBook1 類別不會定義建構函式。 相反地,它會使用自動預設建構函式。

class ExampleBook1 {
    [string]   $Name
    [string]   $Author
    [int]      $Pages
    [datetime] $PublishedOn
}

[ExampleBook1]::new()
Name Author Pages PublishedOn
---- ------ ----- -----------
                0 1/1/0001 12:00:00 AM

注意

Name 和 Author 屬性的預設值是$null因為它們的類型為字串,這是參考類型。 其他屬性具有其定義類型的預設值,因為它們是實值類型屬性。 如需屬性預設值的詳細資訊,請參閱 about_Classes_Properties中的。

範例 2 - 覆寫預設建構函式

ExampleBook2 明確定義預設建構函式,將 PublishedOn 的值設定為目前的日期,並將 Pages 的值設定為 1

class ExampleBook2 {
    [string]   $Name
    [string]   $Author
    [int]      $Pages
    [datetime] $PublishedOn

    ExampleBook2() {
        $this.PublishedOn = (Get-Date).Date
        $this.Pages       = 1
    }
}

[ExampleBook2]::new()
Name Author Pages PublishedOn
---- ------ ----- -----------
                1 11/1/2023 12:00:00 AM

範例 3 - 定義建構函式多載

ExampleBook3 類別會定義三個建構函式多載,讓用戶能夠從哈希表建立類別的實例、傳遞每個屬性值,以及傳遞書籍和作者的名稱。 類別不會定義預設建構函式。

class ExampleBook3 {
    [string]   $Name
    [string]   $Author
    [int]      $Pages
    [datetime] $PublishedOn

    ExampleBook3([hashtable]$Info) {
        switch ($Info.Keys) {
            'Name'        { $this.Name        = $Info.Name }
            'Author'      { $this.Author      = $Info.Author }
            'Pages'       { $this.Pages       = $Info.Pages }
            'PublishedOn' { $this.PublishedOn = $Info.PublishedOn }
        }
    }

    ExampleBook3(
        [string]   $Name,
        [string]   $Author,
        [int]      $Pages,
        [datetime] $PublishedOn
    ) {
        $this.Name        = $Name
        $this.Author      = $Author
        $this.Pages       = $Pages
        $this.PublishedOn = $PublishedOn
    }

    ExampleBook3([string]$Name, [string]$Author) {
        $this.Name   = $Name
        $this.Author = $Author
    }
}

[ExampleBook3]::new(@{
    Name        = 'The Hobbit'
    Author      = 'J.R.R. Tolkien'
    Pages       = 310
    PublishedOn = '1937-09-21'
})
[ExampleBook3]::new('The Hobbit', 'J.R.R. Tolkien', 310, '1937-09-21')
[ExampleBook3]::new('The Hobbit', 'J.R.R. Tolkien')
[ExampleBook3]::new()
Name       Author         Pages PublishedOn
----       ------         ----- -----------
The Hobbit J.R.R. Tolkien   310 9/21/1937 12:00:00 AM
The Hobbit J.R.R. Tolkien   310 9/21/1937 12:00:00 AM
The Hobbit J.R.R. Tolkien     0 1/1/0001 12:00:00 AM

MethodException:
Line |
  42 |  [ExampleBook3]::new()
     |  ~~~~~~~~~~~~~~~~~~~~~
     | Cannot find an overload for "new" and the argument count: "0".

呼叫預設建構函式會傳回方法例外狀況。 只有在類別未定義任何建構函式時,才會定義類別的自動預設建構函式。 由於 ExampleBook3 會定義多個多載,因此預設建構函式不會自動新增至 類別。

範例 4 - 使用共用方法鏈結建構函式

此範例示範如何為建構函式撰寫可重複使用的共享程序代碼。 PowerShell 類別無法使用建構函式鏈結,因此此範例類別會改為定義 Init() 方法。 方法有數個多載。 具有較少參數的多載會呼叫具有未指定參數預設值的更明確多載。

class ExampleBook4 {
    [string]   $Name
    [string]   $Author
    [datetime] $PublishedOn
    [int]      $Pages

    ExampleBook4() {
        $this.Init()
    }
    ExampleBook4([string]$Name) {
        $this.Init($Name)
    }
    ExampleBook4([string]$Name, [string]$Author) {
        $this.Init($Name, $Author)
    }
    ExampleBook4([string]$Name, [string]$Author, [datetime]$PublishedOn) {
        $this.Init($Name, $Author, $PublishedOn)
    }
    ExampleBook4(
      [string]$Name,
      [string]$Author,
      [datetime]$PublishedOn,
      [int]$Pages
    ) {
        $this.Init($Name, $Author, $PublishedOn, $Pages)
    }

    hidden Init() {
        $this.Init('Unknown')
    }
    hidden Init([string]$Name) {
        $this.Init($Name, 'Unknown')
    }
    hidden Init([string]$Name, [string]$Author) {
        $this.Init($Name, $Author, (Get-Date).Date)
    }
    hidden Init([string]$Name, [string]$Author, [datetime]$PublishedOn) {
        $this.Init($Name, $Author, $PublishedOn, 1)
    }
    hidden Init(
        [string]$Name,
        [string]$Author,
        [datetime]$PublishedOn,
        [int]$Pages
      ) {
        $this.Name        = $Name
        $this.Author      = $Author
        $this.PublishedOn = $PublishedOn
        $this.Pages       = $Pages
    }
}

[ExampleBook4]::new()
[ExampleBook4]::new('The Hobbit')
[ExampleBook4]::new('The Hobbit', 'J.R.R. Tolkien')
[ExampleBook4]::new('The Hobbit', 'J.R.R. Tolkien', (Get-Date '1937-9-21'))
[ExampleBook4]::new(
    'The Hobbit',
    'J.R.R. Tolkien',
    (Get-Date '1937-9-21'),
    310
)
Name       Author         PublishedOn           Pages
----       ------         -----------           -----
Unknown    Unknown        11/1/2023 12:00:00 AM     1
The Hobbit Unknown        11/1/2023 12:00:00 AM     1
The Hobbit J.R.R. Tolkien 11/1/2023 12:00:00 AM     1
The Hobbit J.R.R. Tolkien 9/21/1937 12:00:00 AM     1
The Hobbit J.R.R. Tolkien 9/21/1937 12:00:00 AM   310

範例 5 - 衍生類別建構函式

下列範例使用類別來定義基類的靜態、預設和參數化建構函式,以及繼承自基類的衍生類別。

class BaseExample {
    static [void] DefaultMessage([type]$Type) {
        Write-Verbose "[$($Type.Name)] default constructor"
    }

    static [void] StaticMessage([type]$Type) {
        Write-Verbose "[$($Type.Name)] static constructor"
    }

    static [void] ParamMessage([type]$Type, [object]$Value) {
        Write-Verbose "[$($Type.Name)] param constructor ($Value)"
    }

    static BaseExample() { [BaseExample]::StaticMessage([BaseExample])  }
    BaseExample()        { [BaseExample]::DefaultMessage([BaseExample]) }
    BaseExample($Value)  { [BaseExample]::ParamMessage([BaseExample], $Value) }
}

class DerivedExample : BaseExample {
    static DerivedExample() { [BaseExample]::StaticMessage([DerivedExample])  }
           DerivedExample() { [BaseExample]::DefaultMessage([DerivedExample]) }

    DerivedExample([int]$Number) : base($Number) {
        [BaseExample]::ParamMessage([DerivedExample], $Number)
    }
    DerivedExample([string]$String) {
        [BaseExample]::ParamMessage([DerivedExample], $String)
    }
}

下列區塊顯示呼叫基類建構函式的詳細資訊傳訊。 只有在第一次建立 類別的實例時,才會發出靜態建構函式訊息。

PS> $VerbosePreference = 'Continue'
PS> $b = [BaseExample]::new()

VERBOSE: [BaseExample] static constructor
VERBOSE: [BaseExample] default constructor

PS> $b = [BaseExample]::new()

VERBOSE: [BaseExample] default constructor

PS> $b = [BaseExample]::new(1)

VERBOSE: [BaseExample] param constructor (1)

下一個區塊會顯示在新的工作階段中呼叫衍生類別建構函式的詳細資訊傳訊。 第一次呼叫衍生類別建構函式時,會呼叫基類和衍生類別的靜態建構函式。 會話中不會再次呼叫這些建構函式。 基類的建構函式一律會在衍生類別的建構函式之前執行。

PS> $VerbosePreference = 'Continue'
PS> $c = [DerivedExample]::new()

VERBOSE: [BaseExample] static constructor
VERBOSE: [DerivedExample] static constructor
VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] default constructor

PS> $c = [DerivedExample]::new()

VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] default constructor

PS> $c = [DerivedExample]::new(1)

VERBOSE: [BaseExample] param constructor (1)
VERBOSE: [DerivedExample] param constructor (1)

PS> $c = [DerivedExample]::new('foo')

VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] param constructor (foo)

建構函式執行順序

類別具現化時,會執行一或多個建構函式的程序代碼。

對於未繼承自另一個類別的類別,順序為:

  1. 類別的靜態建構函式。
  2. 類別適用的建構函式多載。

對於繼承自另一個類別的衍生類別,排序為:

  1. 基類的靜態建構函式。
  2. 衍生類別的靜態建構函式。
  3. 如果衍生類別建構函式明確呼叫基底建構函式多載,它會執行基類的建構函式。 如果未明確呼叫基底建構函式,它會執行基類的預設建構函式。
  4. 衍生類別適用的建構函式多載。

在所有情況下,靜態建構函式只會在會話中執行一次。

如需建構函式行為和排序的範例,請參閱 範例 5

隱藏的建構函式

您可以使用 關鍵詞來宣告類別的建構函式,以隱藏類別的 hidden 建構函式。 隱藏的類別建構函式如下:

  • 不包含在 類別的預設輸出中。
  • Cmdlet 所 Get-Member 傳回的類別成員清單中未包含。 若要使用 Get-Member顯示隱藏的屬性,請使用 Force 參數。
  • 除非完成發生在定義隱藏屬性的類別中,否則不會顯示在索引標籤完成或 IntelliSense 中。
  • 類別的公用成員。 您可以存取和修改它們。 隱藏屬性不會讓它成為私人屬性。 它只會隱藏如先前點中所述的屬性。

注意

當您隱藏任何建構函式時,選項 new() 會從 IntelliSense 和完成結果中移除。

如需 關鍵詞的詳細資訊 hidden ,請參閱 about_Hidden

靜態建構函式

您可以使用 關鍵詞宣告 static 建構函式,將建構函式定義為屬於類別本身,而不是類別的實例。 靜態類別建構函式:

  • 只會在第一次在工作階段中建立 類別的實體時叫用 。
  • 不能有任何參數。
  • 無法使用 變數存取實例屬性或方法 $this

衍生類別的建構函式

當類別繼承自另一個類別時,建構函式可以使用 關鍵詞從基類叫用 base 建構函式。 如果衍生類別未從基類明確叫用建構函式,則會改為叫用基類的預設建構函式。

若要叫用非預設基底建構函式,請在建構函式參數和主體區塊之前新增 : base(<parameters>)

class <derived-class> : <base-class> {
    <derived-class>(<derived-parameters>) : <base-class>(<base-parameters>) {
        # initialization code
    }
}

定義呼叫基類建構函式的建構函式時,參數可以是下列任一專案:

  • 衍生類別建構函式上任何參數的變數。
  • 任何靜態值。
  • 評估為參數型別值的任何表達式。

如需衍生類別上建構函式的範例,請參閱 範例 5

鏈結建構函式

與 C# 不同,PowerShell 類別建構函式無法搭配 : this(<parameters>) 語法使用鏈結。 若要減少程式代碼重複,請使用具有多個多載的隱藏 Init() 方法,以產生相同的效果。 範例 4 顯示使用此模式的類別。

使用 Update-TypeData 新增實例屬性和方法

除了直接在類別定義中宣告屬性和方法之外,您還可以使用 Update-TypeData Cmdlet 在靜態建構函式中定義類別實例的屬性。

使用此代碼段作為模式的起點。 視需要取代角括弧中的佔位元文字。

class <class-name> {
    static [hashtable[]] $MemberDefinitions = @(
        @{
            Name       = '<member-name>'
            MemberType = '<member-type>'
            Value      = <member-definition>
        }
    )

    static <class-name>() {
        $TypeName = [<class-name>].Name
        foreach ($Definition in [<class-name>]::MemberDefinitions) {
            Update-TypeData -TypeName $TypeName @Definition
        }
    }
}

提示

Cmdlet Add-Member 可以將屬性和方法新增至非靜態建構函式中的類別,但每次呼叫建構函式時都會執行 Cmdlet。 在 Update-TypeData 靜態建構函式中使用 可確保將成員加入類別的程式代碼只需要在會話中執行一次。

只有在無法使用 Update-TypeData定義屬性時,才能將屬性新增至非靜態建構函式中的 類別,例如只讀屬性。

如需使用 定義實例方法 Update-TypeData的詳細資訊,請參閱 about_Classes_Methods。 如需使用 定義實例屬性 Update-TypeData的詳細資訊,請參閱 about_Classes_Properties

限制

PowerShell 類別建構函式具有下列限制:

  • 未實作建構函式鏈結。

    因應措施:定義隱藏 Init() 的方法,並從建構函式內呼叫它們。

  • 建構函式參數無法使用任何屬性,包括驗證屬性。

    因應措施:使用驗證屬性重新指派建構函式主體中的參數。

  • 建構函式參數無法定義預設值。 參數一律為必要參數。

    因應措施:無。

  • 如果隱藏任何建構函式的多載,建構函式的每個多載也會被視為隱藏。

    因應措施:無。

另請參閱