当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 关于类构造函数

作者:精品下载站 日期:2024-12-14 02:24:14 浏览:11 分类:玩电脑

关于类构造函数


简短描述

描述如何定义 PowerShell 类的构造函数。

详细描述

构造函数使您能够在创建类实例时设置默认值并验证对象逻辑。构造函数与类具有相同的名称。构造函数可能有参数,用于初始化新对象的数据成员。

PowerShell 类构造函数被定义为类上的特殊方法。它们的行为与 PowerShell 类方法相同,但有以下例外:

  • 构造函数没有输出类型。他们不能使用 return 关键字。
  • 构造函数始终与类具有相同的名称。
  • 构造函数不能直接调用。它们仅在创建实例时运行。
  • 构造函数永远不会出现在 Get-Member cmdlet 的输出中。

有关 PowerShell 类方法的详细信息,请参阅 about_Classes_Methods。

该类可以定义零个或多个构造函数。如果未定义构造函数,则为该类提供默认的无参数构造函数。此构造函数将所有成员初始化为其默认值。对象类型和字符串被赋予空值。定义构造函数时,不会创建默认的无参数构造函数。如果需要,创建一个无参数构造函数。

您还可以定义无参数静态构造函数。

句法

类构造函数使用以下语法:

默认构造函数语法

<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

笔记

NameAuthor 属性的默认值为 $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 关键字声明类的构造函数来隐藏它们。隐藏类构造函数是:

  • 不包含在该类的默认输出中。
  • 不包含在 Get-Member cmdlet 返回的类成员列表中。要使用 Get-Member 显示隐藏属性,请使用 Force 参数。
  • 除非完成发生在定义隐藏属性的类中,否则不会在制表符完成或 IntelliSense 中显示。
  • 班级的公共成员。可以访问和修改它们。隐藏财产并不意味着它是私有的。它仅隐藏前面几点所述的属性。

笔记

当您隐藏任何构造函数时,new() 选项将从 IntelliSense 和完成结果中删除。

有关 hidden 关键字的更多信息,请参阅 about_Hidden。

静态构造函数

您可以通过使用 static 关键字声明构造函数,将构造函数定义为属于类本身而不是类的实例。静态类构造函数:

  • 仅在会话中第一次创建该类的实例时调用。
  • 不能有任何参数。
  • 无法使用 $this 变量访问实例属性或方法。

派生类的构造函数

当一个类从另一个类继承时,构造函数可以使用 base 关键字从基类调用构造函数。如果派生类没有显式调用基类的构造函数,它将调用基类的默认构造函数。

要调用非默认基本构造函数,请在构造函数参数之后、主体块之前添加 : base()

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

定义调用基类构造函数的构造函数时,参数可以是以下任意项:

  • 派生类构造函数中任何参数的变量。
  • 任何静态值。
  • 任何计算结果为参数类型值的表达式。

有关派生类的构造函数的示例,请参见示例 5。

链接构造函数

与 C# 不同,PowerShell 类构造函数不能使用 : this() 语法进行链接。要减少代码重复,请使用隐藏的 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
        }
    }
}

有用的提示

Add-Member cmdlet 可以向非静态构造函数中的类添加属性和方法,但每次调用构造函数时都会运行该 cmdlet。在静态构造函数中使用 Update-TypeData 可确保将成员添加到类的代码只需在会话中运行一次。

仅当无法使用 Update-TypeData 定义属性(如只读属性)时,才在非静态构造函数中向类添加属性。

有关使用 Update-TypeData 定义实例方法的更多信息,请参阅 about_Classes_Methods。有关使用 Update-TypeData 定义实例属性的更多信息,请参阅 about_Classes_Properties。

局限性

PowerShell 类构造函数具有以下限制:

  • 未实现构造函数链。

    解决方法:定义隐藏的 Init() 方法并从构造函数中调用它们。

  • 构造函数参数不能使用任何属性,包括验证属性。

    解决方法:使用验证属性重新分配构造函数主体中的参数。

  • 构造函数参数不能定义默认值。参数始终是强制性的。

    解决方法:无。

  • 如果隐藏构造函数的任何重载,则构造函数的每个重载也将被视为隐藏。

    解决方法:无。

参见

  • 关于课程
  • about_Classes_Inheritance
  • about_类_方法
  • about_Classes_Properties

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯