Compartilhar via


about_Classes_Inheritance

Descrição breve

Descreve como você pode definir classes que estendem outros tipos.

Descrição longa

As classes do PowerShell dão suporte à herança, o que permite definir uma classe filho que reutiliza (herda), estende ou modifica o comportamento de uma classe pai. A classe cujos membros são herdados é chamada de classe base. A classe que herda os membros da classe base é chamada de classe derivada.

O PowerShell dá suporte apenas à herança única. Uma classe só pode herdar de uma única classe. No entanto, a herança é transitiva, o que permite que você defina uma hierarquia de herança para um conjunto de tipos. Em outras palavras, o tipo D pode herdar do tipo C, que herda do tipo B, que herda do tipo de classe base A. Como a herança é transitiva, os membros do tipo A estão disponíveis para o tipo D.

Classes derivadas não herdam todos os membros da classe base. Os seguintes membros não são herdados:

  • Construtores estáticos, que inicializam os dados estáticos de uma classe.
  • Construtores de instância, que você chama para criar uma nova instância da classe. Cada classe deve definir seus próprios construtores.

Você pode estender uma classe criando uma nova classe que deriva de uma classe existente. A classe derivada herda as propriedades e os métodos da classe base. Você pode adicionar ou substituir os membros da classe base conforme necessário.

As classes também podem herdar de interfaces, que definem um contrato. Uma classe que herda de uma interface deve implementar esse contrato. Quando isso acontece, a classe é utilizável como qualquer outra classe que implementa essa interface. Se uma classe herdar de uma interface, mas não implementar a interface , o PowerShell gerará um erro de análise para a classe .

Alguns operadores do PowerShell dependem de uma classe implementando uma interface específica. Por exemplo, o -eq operador verifica apenas a igualdade de referência, a menos que a classe implemente a interface System.IEquatable . Os -leoperadores , -lt, -gee -gt só funcionam em classes que implementam a interface System.IComparable .

Uma classe derivada usa a : sintaxe para estender uma classe base ou implementar interfaces. A classe derivada sempre deve ser mais à esquerda na declaração de classe.

Este exemplo mostra a sintaxe básica de herança de classe do PowerShell.

Class Derived : Base {...}

Este exemplo mostra a herança com uma declaração de interface que vem após a classe base.

Class Derived : Base, Interface {...}

Syntax

A herança de classe usa as seguintes sintaxes:

Sintaxe de uma linha

class <derived-class-name> : <base-class-or-interface-name>[, <interface-name>...] {
    <derived-class-body>
}

Por exemplo:

# Base class only
class Derived : Base {...}
# Interface only
class Derived : System.IComparable {...}
# Base class and interface
class Derived : Base, System.IComparable {...}

Sintaxe multilinha

class <derived-class-name> : <base-class-or-interface-name>[,
    <interface-name>...] {
    <derived-class-body>
}

Por exemplo:

class Derived : Base,
                System.IComparable,
                System.IFormattable,
                System.IConvertible {
    # Derived class definition
}

Exemplos

Exemplo 1 – Herdando e substituindo de uma classe base

O exemplo a seguir mostra o comportamento das propriedades herdadas com e sem substituição. Execute os blocos de código em ordem depois de ler sua descrição.

Definindo a classe base

O primeiro bloco de código define PublishedWork como uma classe base. Tem duas propriedades estáticas, Lista e Artistas. Em seguida, ele define o método estático RegisterWork() para adicionar obras à propriedade Static List e aos artistas à propriedade Artists , escrevendo uma mensagem para cada nova entrada nas listas.

A classe define três propriedades de instância que descrevem um trabalho publicado. Por fim, ele define os Register() métodos de instância e ToString() .

class PublishedWork {
    static [PublishedWork[]] $List    = @()
    static [string[]]        $Artists = @()

    static [void] RegisterWork([PublishedWork]$Work) {
        $wName   = $Work.Name
        $wArtist = $Work.Artist
        if ($Work -notin [PublishedWork]::List) {
            Write-Verbose "Adding work '$wName' to works list"
            [PublishedWork]::List += $Work
        } else {
            Write-Verbose "Work '$wName' already registered."
        }
        if ($wArtist -notin [PublishedWork]::Artists) {
            Write-Verbose "Adding artist '$wArtist' to artists list"
            [PublishedWork]::Artists += $wArtist
        } else {
            Write-Verbose "Artist '$wArtist' already registered."
        }
    }

    static [void] ClearRegistry() {
        Write-Verbose "Clearing PublishedWork registry"
        [PublishedWork]::List    = @()
        [PublishedWork]::Artists = @()
    }

    [string] $Name
    [string] $Artist
    [string] $Category

    [void] Init([string]$WorkType) {
        if ([string]::IsNullOrEmpty($this.Category)) {
            $this.Category = "${WorkType}s"
        }
    }

    PublishedWork() {
        $WorkType = $this.GetType().FullName
        $this.Init($WorkType)
        Write-Verbose "Defined a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist as a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist, [string]$Category) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist ($Category) as a published work of type [$WorkType]"
    }

    [void]   Register() { [PublishedWork]::RegisterWork($this) }
    [string] ToString() { return "$($this.Name) by $($this.Artist)" }
}

Definindo uma classe derivada sem substituições

A primeira classe derivada é Album. Ele não substitui nenhuma propriedade ou método. Ele adiciona uma nova propriedade de instância, Genres, que não existe na classe base.

class Album : PublishedWork {
    [string[]] $Genres   = @()
}

O bloco de código a seguir mostra o comportamento da classe Album derivada. Primeiro, ele define o $VerbosePreference para que as mensagens dos métodos de classe emitam no console. Ele cria três instâncias da classe , mostra-as em uma tabela e, em seguida, registra-as com o método estático RegisterWork() herdado. Em seguida, ele chama o mesmo método estático na classe base diretamente.

$VerbosePreference = 'Continue'
$Albums = @(
    [Album]@{
        Name   = 'The Dark Side of the Moon'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Psychedelic rock'
    }
    [Album]@{
        Name   = 'The Wall'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Art rock'
    }
    [Album]@{
        Name   = '36 Chambers'
        Artist = 'Wu-Tang Clan'
        Genres = 'Hip hop'
    }
)

$Albums | Format-Table
$Albums | ForEach-Object { [Album]::RegisterWork($_) }
$Albums | ForEach-Object { [PublishedWork]::RegisterWork($_) }
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]

Genres                               Name                      Artist       Category
------                               ----                      ------       --------
{Progressive rock, Psychedelic rock} The Dark Side of the Moon Pink Floyd   Albums
{Progressive rock, Art rock}         The Wall                  Pink Floyd   Albums
{Hip hop}                            36 Chambers               Wu-Tang Clan Albums

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

VERBOSE: Work 'The Dark Side of the Moon' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work 'The Wall' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work '36 Chambers' already registered.
VERBOSE: Artist 'Wu-Tang Clan' already registered.

Observe que, embora a classe Album não tenha definido um valor para Category ou construtores, a propriedade foi definida pelo construtor padrão da classe base.

No sistema de mensagens detalhadas, a segunda chamada para o RegisterWork() método informa que as obras e os artistas já estão registrados. Embora a primeira chamada para RegisterWork() tenha sido para a classe Album derivada, ela usou o método estático herdado da classe base PublishedWork . Esse método atualizou as propriedades list e artist estáticas na classe base, que a classe derivada não substituiu.

O próximo bloco de código limpa o registro e chama o método de Register() instância nos objetos Album .

[PublishedWork]::ClearRegistry()
$Albums.Register()
VERBOSE: Clearing PublishedWork registry

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

O método de instância nos objetos Album tem o mesmo efeito que chamar o método estático na classe derivada ou base.

O bloco de código a seguir compara as propriedades estáticas para a classe base e a classe derivada, mostrando que elas são as mesmas.

[pscustomobject]@{
    '[PublishedWork]::List'    = [PublishedWork]::List -join ",`n"
    '[Album]::List'            = [Album]::List -join ",`n"
    '[PublishedWork]::Artists' = [PublishedWork]::Artists -join ",`n"
    '[Album]::Artists'         = [Album]::Artists -join ",`n"
    'IsSame::List'             = (
        [PublishedWork]::List.Count -eq [Album]::List.Count -and
        [PublishedWork]::List.ToString() -eq [Album]::List.ToString()
    )
    'IsSame::Artists'          = (
        [PublishedWork]::Artists.Count -eq [Album]::Artists.Count -and
        [PublishedWork]::Artists.ToString() -eq [Album]::Artists.ToString()
    )
} | Format-List
[PublishedWork]::List    : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[Album]::List            : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[PublishedWork]::Artists : Pink Floyd,
                           Wu-Tang Clan
[Album]::Artists         : Pink Floyd,
                           Wu-Tang Clan
IsSame::List             : True
IsSame::Artists          : True

Definindo uma classe derivada com substituições

O próximo bloco de código define a classe Illustration herdando da classe base PublishedWork . A nova classe estende a classe base definindo a propriedade Instância média com um valor padrão de Unknown.

Ao contrário da classe Album derivada, Illustration substitui as seguintes propriedades e métodos:

  • Ele substitui a propriedade artistas estáticos . A definição é a mesma, mas a classe Ilustração a declara diretamente.
  • Ele substitui a propriedade de instância Category , definindo o valor padrão como Illustrations.
  • Ele substitui o método de ToString() instância para que a representação de cadeia de caracteres de uma ilustração inclua o meio com o qual foi criada.

A classe também define o método estático RegisterIllustration() para primeiro chamar o método de classe RegisterWork() base e, em seguida, adicionar o artista à propriedade estática Artists substituída na classe derivada.

Por fim, a classe substitui todos os três construtores:

  1. O construtor padrão está vazio, exceto por uma mensagem detalhada que indica que ele criou uma ilustração.
  2. O próximo construtor usa dois valores de cadeia de caracteres para o nome e o artista que criaram a ilustração. Em vez de implementar a lógica para definir as propriedades Name e Artist , o construtor chama o construtor apropriado da classe base.
  3. O último construtor usa três valores de cadeia de caracteres para o nome, o artista e o meio da ilustração. Ambos os construtores gravam uma mensagem detalhada indicando que eles criaram uma ilustração.
class Illustration : PublishedWork {
    static [string[]] $Artists = @()

    static [void] RegisterIllustration([Illustration]$Work) {
        $wArtist = $Work.Artist

        [PublishedWork]::RegisterWork($Work)

        if ($wArtist -notin [Illustration]::Artists) {
            Write-Verbose "Adding illustrator '$wArtist' to artists list"
            [Illustration]::Artists += $wArtist
        } else {
            Write-Verbose "Illustrator '$wArtist' already registered."
        }
    }

    [string] $Category = 'Illustrations'
    [string] $Medium   = 'Unknown'

    [string] ToString() {
        return "$($this.Name) by $($this.Artist) ($($this.Medium))"
    }

    Illustration() {
        Write-Verbose 'Defined an illustration'
    }

    Illustration([string]$Name, [string]$Artist) : base($Name, $Artist) {
        Write-Verbose "Defined '$Name' by $Artist ($($this.Medium)) as an illustration"
    }

    Illustration([string]$Name, [string]$Artist, [string]$Medium) {
        $this.Name = $Name
        $this.Artist = $Artist
        $this.Medium = $Medium

        Write-Verbose "Defined '$Name' by $Artist ($Medium) as an illustration"
    }
}

O bloco de código a seguir mostra o comportamento da classe Ilustração derivada. Ele cria três instâncias da classe , mostra-as em uma tabela e, em seguida, registra-as com o método estático RegisterWork() herdado. Em seguida, ele chama o mesmo método estático na classe base diretamente. Por fim, ele grava mensagens mostrando a lista de artistas registrados para a classe base e a classe derivada.

$Illustrations = @(
    [Illustration]@{
        Name   = 'The Funny Thing'
        Artist = 'Wanda Gág'
        Medium = 'Lithography'
    }
    [Illustration]::new('Millions of Cats', 'Wanda Gág')
    [Illustration]::new(
      'The Lion and the Mouse',
      'Jerry Pinkney',
      'Watercolor'
    )
)

$Illustrations | Format-Table
$Illustrations | ForEach-Object { [Illustration]::RegisterIllustration($_) }
$Illustrations | ForEach-Object { [PublishedWork]::RegisterWork($_) }
"Published work artists: $([PublishedWork]::Artists -join ', ')"
"Illustration artists: $([Illustration]::Artists -join ', ')"
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined an illustration
VERBOSE: Defined 'Millions of Cats' by Wanda Gág as a published work of type [Illustration]
VERBOSE: Defined 'Millions of Cats' by Wanda Gág (Unknown) as an illustration
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined 'The Lion and the Mouse' by Jerry Pinkney (Watercolor) as an illustration

Category      Medium      Name                   Artist
--------      ------      ----                   ------
Illustrations Lithography The Funny Thing        Wanda Gág
Illustrations Unknown     Millions of Cats       Wanda Gág
Illustrations Watercolor  The Lion and the Mouse Jerry Pinkney

VERBOSE: Adding work 'The Funny Thing' to works list
VERBOSE: Adding artist 'Wanda Gág' to artists list
VERBOSE: Adding illustrator 'Wanda Gág' to artists list
VERBOSE: Adding work 'Millions of Cats' to works list
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Illustrator 'Wanda Gág' already registered.
VERBOSE: Adding work 'The Lion and the Mouse' to works list
VERBOSE: Adding artist 'Jerry Pinkney' to artists list
VERBOSE: Adding illustrator 'Jerry Pinkney' to artists list

VERBOSE: Work 'The Funny Thing' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'Millions of Cats' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'The Lion and the Mouse' already registered.
VERBOSE: Artist 'Jerry Pinkney' already registered.

Published work artists: Pink Floyd, Wu-Tang Clan, Wanda Gág, Jerry Pinkney

Illustration artists: Wanda Gág, Jerry Pinkney

As mensagens detalhadas da criação das instâncias mostram que:

  • Ao criar a primeira instância, o construtor padrão da classe base foi chamado antes do construtor padrão da classe derivada.
  • Ao criar a segunda instância, o construtor herdado explicitamente foi chamado para a classe base antes do construtor de classe derivada.
  • Ao criar a terceira instância, o construtor padrão da classe base foi chamado antes do construtor de classe derivada.

As mensagens detalhadas do RegisterWork() método indicam que as obras e os artistas já estavam registrados. Isso ocorre porque o RegisterIllustration() método chamou o RegisterWork() método internamente.

No entanto, ao comparar o valor da propriedade Artista estático para a classe base e a classe derivada, os valores são diferentes. A propriedade Artists para a classe derivada inclui apenas ilustradores, não os artistas do álbum. Redefinir a propriedade Artist na classe derivada impede que a classe retorne a propriedade estática na classe base.

O bloco de código final chama o ToString() método nas entradas da propriedade List estática na classe base.

[PublishedWork]::List | ForEach-Object -Process { $_.ToString() }
The Dark Side of the Moon by Pink Floyd
The Wall by Pink Floyd
36 Chambers by Wu-Tang Clan
The Funny Thing by Wanda Gág (Lithography)
Millions of Cats by Wanda Gág (Unknown)
The Lion and the Mouse by Jerry Pinkney (Watercolor)

As instâncias de Álbum retornam apenas o nome e o artista em sua cadeia de caracteres. As instâncias de Ilustração também incluíram o meio entre parênteses, pois essa classe substituiu o ToString() método .

Exemplo 2 – Implementando interfaces

O exemplo a seguir mostra como uma classe pode implementar uma ou mais interfaces. O exemplo estende a definição de uma classe Temperature para dar suporte a mais operações e comportamentos.

Definição de classe inicial

Antes de implementar qualquer interface, a classe Temperature é definida com duas propriedades, Degrees e Scale. Ele define construtores e três métodos de instância para retornar a instância como graus de uma escala específica.

A classe define as escalas disponíveis com a enumeração TemperatureScale .

class Temperature {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale   = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5/9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5/9 }
            Kelvin     { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees * 9/5 + 32 }
            Kelvin     { return $this.Degrees * 9/5 - 459.67 }
        }
        return $this.Degrees
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

No entanto, nesta implementação básica, há algumas limitações, conforme mostrado na seguinte saída de exemplo:

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new([TemperatureScale]::Fahrenheit)
$Kelvin     = [Temperature]::new(0, 'Kelvin')

$Celsius, $Fahrenheit, $Kelvin

"The temperatures are: $Celsius, $Fahrenheit, $Kelvin"

[Temperature]::new() -eq $Celsius

$Celsius -gt $Kelvin
Degrees      Scale
-------      -----
   0.00    Celsius
   0.00 Fahrenheit
   0.00     Kelvin

The temperatures are: Temperature, Temperature, Temperature

False

InvalidOperation:
Line |
  11 |  $Celsius -gt $Kelvin
     |  ~~~~~~~~~~~~~~~~~~~~
     | Cannot compare "Temperature" because it is not IComparable.

A saída mostra as instâncias de Temperatura:

  • Não exiba corretamente como cadeias de caracteres.
  • Não é possível verificar corretamente a equivalência.
  • Não pode ser comparado.

Esses três problemas podem ser resolvidos implementando interfaces para a classe .

Implementando IFormattable

A primeira interface a ser implementada para a classe Temperature é System.IFormattable. Essa interface permite a formatação de uma instância da classe como cadeias de caracteres diferentes. Para implementar a interface , a classe precisa herdar de System.IFormattable e definir o método de ToString() instância.

O ToString() método de instância precisa ter a seguinte assinatura:

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # Implementation
}

A assinatura que a interface requer está listada na documentação de referência.

Para Temperatura, a classe deve dar suporte a três formatos: C retornar a instância em Celsius, F retorná-la em Fahrenheit e K retorná-la em Kelvin. Para qualquer outro formato, o método deve gerar um System.FormatException.

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # If format isn't specified, use the defined scale.
    if ([string]::IsNullOrEmpty($Format)) {
        $Format = switch ($this.Scale) {
            Celsius    { 'C' }
            Fahrenheit { 'F' }
            Kelvin     { 'K' }
        }
    }
    # If format provider isn't specified, use the current culture.
    if ($null -eq $FormatProvider) {
        $FormatProvider = [CultureInfo]::CurrentCulture
    }
    # Format the temperature.
    switch ($Format) {
        'C' {
            return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
        }
        'F' {
            return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
        }
        'K' {
            return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
        }
    }
    # If we get here, the format is invalid.
    throw [System.FormatException]::new(
        "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
    )
}

Nessa implementação, o método usa como padrão a escala de instância para o formato e a cultura atual ao formatar o próprio valor de grau numérico. Ele usa os To<Scale>() métodos de instância para converter os graus, formatá-los em casas de duas decimais e acrescenta o símbolo de grau apropriado à cadeia de caracteres.

Com a assinatura necessária implementada, a classe também pode definir sobrecargas para facilitar o retorno da instância formatada.

[string] ToString([string]$Format) {
    return $this.ToString($Format, $null)
}

[string] ToString() {
    return $this.ToString($null, $null)
}

O código a seguir mostra a definição atualizada para Temperatura:

class Temperature : System.IFormattable {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

A saída para as sobrecargas de método é mostrada no bloco a seguir.

$Temp = [Temperature]::new()
"The temperature is $Temp"
$Temp.ToString()
$Temp.ToString('K')
$Temp.ToString('F', $null)
The temperature is 0.00°C

0.00°C

273.15°K

32.00°F

Implementando o IEquatable

Agora que a classe Temperature pode ser formatada para facilitar a leitura, os usuários precisam ser capazes de marcar se duas instâncias da classe são iguais. Para dar suporte a esse teste, a classe precisa implementar a interface System.IEquatable .

Para implementar a interface , a classe precisa herdar de System.IEquatable e definir o método de Equals() instância. O Equals() método precisa ter a seguinte assinatura:

[bool] Equals([object]$Other) {
    # Implementation
}

A assinatura que a interface requer está listada na documentação de referência.

Para Temperatura, a classe só deve dar suporte à comparação de duas instâncias da classe . Para qualquer outro valor ou tipo, incluindo $null, ele deve retornar $false. Ao comparar duas temperaturas, o método deve converter ambos os valores em Kelvin, pois as temperaturas podem ser equivalentes mesmo com escalas diferentes.

[bool] Equals([object]$Other) {
    # If the other object is null, we can't compare it.
    if ($null -eq $Other) {
        return $false
    }

    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        return $false
    }

    # Compare the temperatures as Kelvin.
    return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
}

Com o método de interface implementado, a definição atualizada para Temperature é:

class Temperature : System.IFormattable, System.IEquatable[object] {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }

        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }

        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

O bloco a seguir mostra como a classe atualizada se comporta:

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit) = $($Celsius.Equals($Fahrenheit))
`$Celsius -eq `$Fahrenheit     = $($Celsius -eq $Fahrenheit)
`$Celsius -ne `$Kelvin         = $($Celsius -ne $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K

$Celsius.Equals($Fahrenheit) = True
$Celsius -eq $Fahrenheit     = True
$Celsius -ne $Kelvin         = True

Implementando IComparable

A última interface a ser implementada para a classe Temperature é System.IComparable. Quando a classe implementa essa interface, os usuários podem usar os -ltoperadores , -le-gt, e -ge para comparar instâncias da classe .

Para implementar a interface , a classe precisa herdar de System.IComparable e definir o método de Equals() instância. O Equals() método precisa ter a seguinte assinatura:

[int] CompareTo([Object]$Other) {
    # Implementation
}

A assinatura que a interface requer está listada na documentação de referência.

Para Temperatura, a classe só deve dar suporte à comparação de duas instâncias da classe . Como o tipo subjacente para a propriedade Degrees , mesmo quando convertido em uma escala diferente, é um número de ponto flutuante, o método pode depender do tipo subjacente para a comparação real.

[int] CompareTo([object]$Other) {
    # If the other object's null, consider this instance "greater than" it
    if ($null -eq $Other) {
        return 1
    }
    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        throw [System.ArgumentException]::new(
            "Object must be of type 'Temperature'."
        )
    }
    # Compare the temperatures as Kelvin.
    return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
}

A definição final para a classe Temperature é:

class Temperature : System.IFormattable,
                    System.IComparable,
                    System.IEquatable[object] {
    # Instance properties
    [float]            $Degrees
    [TemperatureScale] $Scale

    # Constructors
    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }
        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }
        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
    [int] CompareTo([object]$Other) {
        # If the other object's null, consider this instance "greater than" it
        if ($null -eq $Other) {
            return 1
        }
        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            throw [System.ArgumentException]::new(
                "Object must be of type 'Temperature'."
            )
        }
        # Compare the temperatures as Kelvin.
        return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

Com a definição completa, os usuários podem formatar e comparar instâncias da classe no PowerShell como qualquer tipo interno.

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit)    = $($Celsius.Equals($Fahrenheit))
`$Celsius.Equals(`$Kelvin)        = $($Celsius.Equals($Kelvin))
`$Celsius.CompareTo(`$Fahrenheit) = $($Celsius.CompareTo($Fahrenheit))
`$Celsius.CompareTo(`$Kelvin)     = $($Celsius.CompareTo($Kelvin))
`$Celsius -lt `$Fahrenheit        = $($Celsius -lt $Fahrenheit)
`$Celsius -le `$Fahrenheit        = $($Celsius -le $Fahrenheit)
`$Celsius -eq `$Fahrenheit        = $($Celsius -eq $Fahrenheit)
`$Celsius -gt `$Kelvin            = $($Celsius -gt $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K
$Celsius.Equals($Fahrenheit)    = True
$Celsius.Equals($Kelvin)        = False
$Celsius.CompareTo($Fahrenheit) = 0
$Celsius.CompareTo($Kelvin)     = 1
$Celsius -lt $Fahrenheit        = False
$Celsius -le $Fahrenheit        = True
$Celsius -eq $Fahrenheit        = True
$Celsius -gt $Kelvin            = True

Exemplo 3 – Herdar de uma classe base genérica

Este exemplo mostra como você pode derivar de uma classe genérica como System.Collections.Generic.List.

Usando uma classe interna como o parâmetro de tipo

Execute o bloco de código a seguir. Ele mostra como uma nova classe pode herdar de um tipo genérico, desde que o parâmetro de tipo já esteja definido no momento da análise.

class ExampleStringList : System.Collections.Generic.List[string] {}

$List = [ExampleStringList]::New()
$List.AddRange([string[]]@('a','b','c'))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleStringList
BaseType : System.Collections.Generic.List`1[System.String]

a
b
c

Usando uma classe personalizada como o parâmetro de tipo

O próximo bloco de código primeiro define uma nova classe, ExampleItem, com uma única propriedade de instância e o ToString() método . Em seguida, ele define a classe ExampleItemList herdando da classe base System.Collections.Generic.List com ExampleItem como o parâmetro de tipo.

Copie todo o bloco de código e execute-o como uma única instrução.

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}
ParentContainsErrorRecordException: An error occurred while creating the pipeline.

A execução de todo o bloco de código gera um erro porque o PowerShell ainda não carregou a classe ExampleItem no runtime. Você ainda não pode usar o nome da classe como o parâmetro de tipo para a classe base System.Collections.Generic.List .

Execute os blocos de código a seguir na ordem em que estão definidos.

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}

Desta vez, o PowerShell não gera erros. Ambas as classes agora estão definidas. Execute o bloco de código a seguir para exibir o comportamento da nova classe.

$List = [ExampleItemList]::New()
$List.AddRange([ExampleItem[]]@(
    [ExampleItem]@{ Name = 'Foo' }
    [ExampleItem]@{ Name = 'Bar' }
    [ExampleItem]@{ Name = 'Baz' }
))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleItemList
BaseType : System.Collections.Generic.List`1[ExampleItem]

Name
----
Foo
Bar
Baz

Derivar um genérico com um parâmetro de tipo personalizado em um módulo

Os blocos de código a seguir mostram como você pode definir uma classe que herda de uma classe base genérica que usa um tipo personalizado para o parâmetro de tipo.

Salve o bloco de código a seguir como GenericExample.psd1.

@{
    RootModule        = 'GenericExample.psm1'
    ModuleVersion     = '0.1.0'
    GUID              = '2779fa60-0b3b-4236-b592-9060c0661ac2'
}

Salve o bloco de código a seguir como GenericExample.InventoryItem.psm1.

class InventoryItem {
    [string] $Name
    [int]    $Count

    InventoryItem() {}
    InventoryItem([string]$Name) {
        $this.Name = $Name
    }
    InventoryItem([string]$Name, [int]$Count) {
        $this.Name  = $Name
        $this.Count = $Count
    }

    [string] ToString() {
        return "$($this.Name) ($($this.Count))"
    }
}

Salve o bloco de código a seguir como GenericExample.psm1.

using namespace System.Collections.Generic
using module ./GenericExample.InventoryItem.psm1

class Inventory : List[InventoryItem] {}

# Define the types to export with type accelerators.
$ExportableTypes =@(
    [InventoryItem]
    [Inventory]
)
# Get the internal TypeAccelerators class to use its static methods.
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
    'System.Management.Automation.TypeAccelerators'
)
# Ensure none of the types would clobber an existing type accelerator.
# If a type accelerator with the same name exists, throw an exception.
$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
foreach ($Type in $ExportableTypes) {
    if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
        $Message = @(
            "Unable to register type accelerator '$($Type.FullName)'"
            'Accelerator already exists.'
        ) -join ' - '

        throw [System.Management.Automation.ErrorRecord]::new(
            [System.InvalidOperationException]::new($Message),
            'TypeAcceleratorAlreadyExists',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $Type.FullName
        )
    }
}
# Add type accelerators for every exportable type.
foreach ($Type in $ExportableTypes) {
    $TypeAcceleratorsClass::Add($Type.FullName, $Type)
}
# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    foreach($Type in $ExportableTypes) {
        $TypeAcceleratorsClass::Remove($Type.FullName)
    }
}.GetNewClosure()

Dica

O módulo raiz adiciona os tipos personalizados aos aceleradores de tipo do PowerShell. Esse padrão permite que os usuários do módulo acessem imediatamente o IntelliSense e o preenchimento automático para os tipos personalizados sem precisar usar a using module instrução primeiro.

Para obter mais informações sobre esse padrão, consulte a seção "Exportando com aceleradores de tipo" de about_Classes.

Importe o módulo e verifique a saída.

Import-Module ./GenericExample.psd1

$Inventory = [Inventory]::new()
$Inventory.GetType() | Format-List -Property Name, BaseType

$Inventory.Add([InventoryItem]::new('Bucket', 2))
$Inventory.Add([InventoryItem]::new('Mop'))
$Inventory.Add([InventoryItem]@{ Name = 'Broom' ; Count = 4 })
$Inventory
Name     : Inventory
BaseType : System.Collections.Generic.List`1[InventoryItem]

Name   Count
----   -----
Bucket     2
Mop        0
Broom      4

O módulo é carregado sem erros porque a classe InventoryItem é definida em um arquivo de módulo diferente da classe Inventory . Ambas as classes estão disponíveis para usuários do módulo.

Herdando uma classe base

Quando uma classe herda de uma classe base, ela herda as propriedades e os métodos da classe base. Ele não herda os construtores de classe base diretamente, mas pode chamá-los.

Quando a classe base é definida no .NET em vez do PowerShell, observe que:

  • As classes do PowerShell não podem herdar de classes lacradas.
  • Ao herdar de uma classe base genérica, o parâmetro de tipo para a classe genérica não pode ser a classe derivada. Usar a classe derivada como o parâmetro de tipo gera um erro de análise.

Para ver como a herança e a substituição funcionam para classes derivadas, consulte Exemplo 1.

Construtores de classe derivada

As classes derivadas não herdam diretamente os construtores da classe base. Se a classe base definir um construtor padrão e a classe derivada não definir nenhum construtor, novas instâncias da classe derivada usarão o construtor padrão da classe base. Se a classe base não definir um construtor padrão, a classe derivada deverá definir explicitamente pelo menos um construtor.

Construtores de classe derivada podem invocar um construtor da classe base com o base palavra-chave. Se a classe derivada não invocar explicitamente um construtor da classe base, ela invocará o construtor padrão para a classe base.

Para invocar um construtor base não padrão, adicione : base(<parameters>) após os parâmetros do construtor e antes do bloco de corpo.

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

Ao definir um construtor que chama um construtor de classe base, os parâmetros podem ser qualquer um dos seguintes itens:

  • A variável de qualquer parâmetro no construtor de classe derivada.
  • Qualquer valor estático.
  • Qualquer expressão que seja avaliada como um valor do tipo de parâmetro.

A classe Ilustração no Exemplo 1 mostra como uma classe derivada pode usar os construtores de classe base.

Métodos de classe derivada

Quando uma classe deriva de uma classe base, ela herda os métodos da classe base e suas sobrecargas. Todas as sobrecargas de método definidas na classe base, incluindo métodos ocultos, estão disponíveis na classe derivada.

Uma classe derivada pode substituir uma sobrecarga de método herdada redefinindo-a na definição de classe. Para substituir a sobrecarga, os tipos de parâmetro devem ser os mesmos da classe base. O tipo de saída para a sobrecarga pode ser diferente.

Ao contrário dos construtores, os métodos não podem usar a : base(<parameters>) sintaxe para invocar uma sobrecarga de classe base para o método . A sobrecarga redefinida na classe derivada substitui completamente a sobrecarga definida pela classe base. Para chamar o método de classe base para uma instância, converta a variável de instância ($this) para a classe base antes de chamar o método .

O snippet a seguir mostra como uma classe derivada pode chamar o método de classe base.

class BaseClass {
    [bool] IsTrue() { return $true }
}
class DerivedClass : BaseClass {
    [bool] IsTrue()     { return $false }
    [bool] BaseIsTrue() { return ([BaseClass]$this).IsTrue() }
}

@"
[BaseClass]::new().IsTrue()        = $([BaseClass]::new().IsTrue())
[DerivedClass]::new().IsTrue()     = $([DerivedClass]::new().IsTrue())
[DerivedClass]::new().BaseIsTrue() = $([DerivedClass]::new().BaseIsTrue())
"@
[BaseClass]::new().IsTrue()        = True
[DerivedClass]::new().IsTrue()     = False
[DerivedClass]::new().BaseIsTrue() = True

Para obter um exemplo estendido mostrando como uma classe derivada pode substituir métodos herdados, consulte a classe Ilustração no Exemplo 1.

Propriedades de classe derivada

Quando uma classe deriva de uma classe base, ela herda as propriedades da classe base. Todas as propriedades definidas na classe base, incluindo propriedades ocultas, estão disponíveis na classe derivada.

Uma classe derivada pode substituir uma propriedade herdada redefinindo-a na definição de classe. A propriedade na classe derivada usa o tipo redefinido e o valor padrão, se houver. Se a propriedade herdada definiu um valor padrão e a propriedade redefinida não, a propriedade herdada não terá nenhum valor padrão.

Se uma classe derivada não substituir uma propriedade estática, acessar a propriedade estática por meio da classe derivada acessará a propriedade estática da classe base. Modificar o valor da propriedade por meio da classe derivada modifica o valor na classe base. Qualquer outra classe derivada que não substitua a propriedade estática também usa o valor da propriedade na classe base. Atualizar o valor de uma propriedade estática herdada em uma classe que não substitui a propriedade pode ter efeitos não intencionais para classes derivadas da mesma classe base.

O exemplo 1 mostra como as classes derivadas herdam, estendem e substituem as propriedades da classe base.

Derivando de genéricos

Quando uma classe deriva de um genérico, o parâmetro de tipo já deve ser definido antes que o PowerShell analise a classe derivada. Se o parâmetro de tipo para o genérico for uma classe ou enumeração do PowerShell definida no mesmo arquivo ou bloco de código, o PowerShell gerará um erro.

Para derivar uma classe de uma classe base genérica com um tipo personalizado como o parâmetro de tipo, defina a classe ou a enumeração para o parâmetro de tipo em um arquivo ou módulo diferente e use a using module instrução para carregar a definição de tipo.

Para obter um exemplo mostrando como herdar de uma classe base genérica, consulte Exemplo 3.

Classes úteis para herdar

Há algumas classes que podem ser úteis para herdar ao criar módulos do PowerShell. Esta seção lista algumas classes base e para que uma classe derivada delas pode ser usada.

  • System.Attribute – deriva classes para definir atributos que podem ser usados para variáveis, parâmetros, definições de classe e enumeração e muito mais.
  • System.Management.Automation.ArgumentTransformationAttribute – deriva classes para manipular a conversão de entrada para uma variável ou parâmetro em um tipo de dados específico.
  • System.Management.Automation.ValidateArgumentsAttribute – deriva classes para aplicar a validação personalizada a variáveis, parâmetros e propriedades de classe.
  • System.Collections.Generic.List – deriva classes para facilitar a criação e o gerenciamento de listas de um tipo de dados específico.
  • System.Exception – deriva classes para definir erros personalizados.

Implementando interfaces

Uma classe do PowerShell que implementa uma interface deve implementar todos os membros dessa interface. Omitir os membros da interface de implementação causa um erro de tempo de análise no script.

Observação

O PowerShell não dá suporte à declaração de novas interfaces no script do PowerShell. Em vez disso, as interfaces devem ser declaradas no código .NET e adicionadas à sessão com o Add-Type cmdlet ou a instrução using assembly .

Quando uma classe implementa uma interface, ela pode ser usada como qualquer outra classe que implemente essa interface. Alguns comandos e operações limitam seus tipos com suporte a classes que implementam uma interface específica.

Para examinar uma implementação de exemplo de interfaces, consulte Exemplo 2.

Interfaces úteis para implementar

Há algumas classes de interface que podem ser úteis para herdar ao criar módulos do PowerShell. Esta seção lista algumas classes base e para que uma classe derivada delas pode ser usada.

  • System.IEquatable – essa interface permite que os usuários comparem duas instâncias da classe . Quando uma classe não implementa essa interface, o PowerShell verifica a equivalência entre duas instâncias usando a igualdade de referência. Em outras palavras, uma instância da classe é igual a si mesma, mesmo que os valores de propriedade em duas instâncias sejam iguais.
  • System.IComparable – essa interface permite que os usuários comparem instâncias da classe com os -leoperadores de comparação , -lt, -gee -gt . Quando uma classe não implementa essa interface, esses operadores geram um erro.
  • System.IFormattable – essa interface permite que os usuários formatem instâncias da classe em cadeias de caracteres diferentes. Isso é útil para classes que têm mais de uma representação de cadeia de caracteres padrão, como itens de orçamento, bibliografias e temperaturas.
  • System.IConvertible – essa interface permite que os usuários convertam instâncias da classe em outros tipos de runtime. Isso é útil para classes que têm um valor numérico subjacente ou podem ser convertidas em um.

Limitações

  • O PowerShell não dá suporte à definição de interfaces no código de script.

    Solução alternativa: defina interfaces em C# e faça referência ao assembly que define as interfaces.

  • As classes do PowerShell só podem herdar de uma classe base.

    Solução alternativa: a herança de classe é transitiva. Uma classe derivada pode herdar de outra classe derivada para obter as propriedades e os métodos de uma classe base.

  • Ao herdar de uma classe ou interface genérica, o parâmetro de tipo para o genérico já deve estar definido. Uma classe não pode se definir como o parâmetro de tipo para uma classe ou interface.

    Solução alternativa: para derivar de uma classe base ou interface genérica, defina o tipo personalizado em um arquivo diferente .psm1 e use a using module instrução para carregar o tipo. Não há solução alternativa para um tipo personalizado usar a si mesmo como o parâmetro de tipo ao herdar de um genérico.

Consulte Também