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 oferece suporte apenas a 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 da classe base tipo A. Como a herança é transitiva, os membros do tipo A estão disponíveis para o tipo D.

As 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 herda de uma interface, mas não implementa a interface, o PowerShell gera um erro de análise para a classe.

Alguns operadores do PowerShell dependem de uma classe que implementa uma interface específica. Por exemplo, o -eq operador só verifica 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 deve sempre 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 depois da classe base.

Class Derived : Base, Interface {...}

Sintaxe

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 - Herdar e substituir de uma classe base

O exemplo a seguir mostra o comportamento de 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. Possui duas propriedades estáticas, Lista e Artistas. Em seguida, ele define o método estático RegisterWork() para adicionar obras à propriedade estática List e os 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. Finalmente, ele define os Register() métodos e ToString() instância.

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 sejam emitidas para o 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 quaisquer construtores, a propriedade foi definida pelo construtor padrão da classe base.

Na segunda chamada para o RegisterWork() método informa que as obras e artistas já estão cadastrados. Embora a primeira chamada para RegisterWork() fosse para a classe Album derivada, ela usava o método estático herdado da classe base PublishedWork. Esse método atualizou as propriedades estáticas List e Artist 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 da classe base e da 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 herdada da classe base PublishedWork . A nova classe estende a classe base definindo a propriedade de instância Medium 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 estática Artists . A definição é a mesma, mas a classe Illustration a declara diretamente.
  • Ele substitui a propriedade de instância Category , definindo o valor padrão como Illustrations.
  • Ele substitui o ToString() método de instância para que a representação de cadeia de caracteres de uma ilustração inclua a mídia com a qual ela 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.

Finalmente, a classe substitui todos os três construtores:

  1. O construtor padrão está vazio, exceto por uma mensagem detalhada indicando 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 criou 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, artista e meio da ilustração. Ambos os construtores escrevem uma mensagem detalhada indicando que 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 Illustration 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. Finalmente, ele escreve 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

O sistema de mensagens detalhado da criação das instâncias mostra que:

  • Ao criar a primeira instância, o construtor padrão de classe base foi chamado antes do construtor padrão de classe derivada.
  • Ao criar a segunda instância, o construtor explicitamente herdado foi chamado para a classe base antes do construtor de classe derivada.
  • Ao criar a terceira instância, o construtor padrão de 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 chamado RegisterWork() o método internamente.

No entanto, ao comparar o valor da propriedade estática Artist 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 ocorrências de Album retornam apenas o nome e o artista em sua cadeia de caracteres. As instâncias Illustration também incluíram a mídia entre parênteses, porque 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 oferecer 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, nessa implementação básica, há algumas limitações, conforme mostrado na saída de exemplo a seguir:

$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 que as instâncias de Temperature:

  • Não é exibido corretamente como cadeias de caracteres.
  • Não é possível verificar corretamente a equivalência.
  • Não dá para comparar.

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 formatar 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 Temperature, a classe deve suportar 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 lançar 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 formato e a cultura atual ao formatar o próprio valor numérico do grau. Ele usa os métodos de To<Scale>() ocorrência para converter os graus, formata-os em duas casas 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 legibilidade, os usuários precisam ser capazes de verificar se duas instâncias da classe são iguais. Para oferecer 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 Equals() método de 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 Temperature, a classe só deve oferecer 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 para Kelvin, uma vez que 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 Temperatura é:

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 o 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, -gte -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 Temperature, a classe só deve oferecer suporte à comparação de duas instâncias da classe. Como o tipo subjacente para a propriedade Graus , mesmo quando convertido em uma escala diferente, é um número de ponto flutuante, o método pode confiar no 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 type

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 type 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 type

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 herdada da classe base System.Collections.Generic.List com ExampleItem como o parâmetro type.

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 do bloco de código inteiro gera um erro porque o PowerShell ainda não carregou a classe ExampleItem no tempo de execução. Ainda não é possível usar o nome da classe como o parâmetro type 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 já 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

Derivando 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 type.

Salve o seguinte bloco de código como GenericExample.psd1.

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

Salve o seguinte bloco de código 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 seguinte bloco de código 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" do 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 seladas.
  • Ao herdar de uma classe base genérica, o parâmetro type para a classe genérica não pode ser a classe derivada. Usar a classe derivada como o parâmetro type gera um erro de análise.

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

Construtores de classe derivada

As classes derivadas não herdam diretamente os construtores da classe base. Se a classe base define um construtor padrão e a classe derivada não define nenhum construtor, novas instâncias da classe derivada usam 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.

Os construtores de classe derivada podem invocar um construtor da classe base com a 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 body.

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 Illustration no Exemplo 1 mostra como uma classe derivada pode usar os construtores de classe base.

Métodos de classe derivados

Quando uma classe deriva de uma classe base, ela herda os métodos da classe base e suas sobrecargas. Quaisquer 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 trecho 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 Illustration no Exemplo 1.

Propriedades de classe derivadas

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á 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 static 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 substitua a propriedade pode ter efeitos não intencionais para classes derivadas da mesma classe base.

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

Derivados de genéricos

Quando uma classe deriva de um genérico, o parâmetro type já deve estar definido antes que o PowerShell analise a classe derivada. Se o parâmetro type 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 type, defina a classe ou enumeração para o parâmetro type 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 o 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 de uma variável ou parâmetro em um tipo de dados específico.
  • System.Management.Automation.ValidateArgumentsAttribute - Deriva classes para aplicar 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 oferece 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 using assembly instrução.

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

Para revisar um exemplo de implementação de interfaces, consulte o 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 só é igual a si mesma, mesmo que os valores de propriedade em duas instâncias sejam os mesmos.
  • System.IComparable - Essa interface permite que os usuários comparem instâncias da classe com os -leoperadores , -lt, -gee -gt comparação. 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 tempo de execução. Isso é útil para classes que têm um valor numérico subjacente ou podem ser convertidas em um.

Limitações

  • O PowerShell não oferece 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 métodos de uma classe base.

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

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

Confira também