about_Classes_Inheritance
Breve descrição
Descreve como você pode definir classes que estendem outros tipos.
Descrição longa
As classes do PowerShell dão suporte à herança, que permite definir uma classe filha 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 definir 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 -le
operadores , -lt
, -ge
e -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 ser sempre 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 de várias linhas
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.
Definição da classe base
O primeiro bloco de código define PublishedWork como uma classe base. Tem duas propriedades estáticas, List e Artists. Em seguida, ele define o método estático RegisterWork()
para adicionar trabalhos à propriedade List estática 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
modo para que as mensagens dos métodos de classe emitam 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 qualquer construtor, a propriedade foi definida pelo construtor padrão da classe base.
Na mensagem detalhada, a segunda chamada para o RegisterWork()
método informa que as obras e artistas já estão registrados. Embora a primeira chamada tenha RegisterWork()
sido para a classe Album derivada, ela usou 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 da 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:
- O construtor padrão está vazio, exceto por uma mensagem detalhada indicando que criou uma ilustração.
- 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.
- 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 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 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
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 explicitamente herdado 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 artistas já estavam registrados. Isso ocorre porque o RegisterIllustration()
método chamado o RegisterWork()
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 o meio entre parênteses, porque essa classe substituiu o ToString()
método.
Exemplo 2 - Implementação de 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 da classe inicial
Antes de implementar quaisquer interfaces, 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 Temperatura:
- Não exiba corretamente como strings.
- 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 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 ToString()
método de 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
devolvê-la em Fahrenheit e K
devolvê-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'"
)
}
Nesta implementação, o método assume 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 métodos de To<Scale>()
instância para converter os graus, formata-os para 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 suportar 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 suportar a 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 -lt
operadores , -le
, -gt
e para -ge
comparar instâncias da classe.
Para implementar a interface, a classe precisa herdar de System.IComparable e definir o Equals()
método de 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 suportar a comparação de duas instâncias da classe. Como o tipo subjacente para a propriedade Degrees , mesmo quando convertido para 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 seguinte bloco de código. 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 de todo o bloco de código 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 seguintes blocos de código na ordem em que sã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 estão agora definidas. Execute o seguinte bloco de código 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()
Gorjeta
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 preencham automaticamente 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 derivados 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. 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 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 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á valor padrão.
Se uma classe derivada não substituir uma propriedade estática, o acesso à propriedade static por meio da classe derivada acessará a propriedade static 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 substitui a propriedade pode ter efeitos não intencionais para classes derivadas da mesma classe base.
O Exemplo 1 mostra como 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 ser 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 - Derive 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 - Derive classes para aplicar validação personalizada a variáveis, parâmetros e propriedades de classe.
- System.Collections.Generic.List - Derive classes para facilitar a criação e o gerenciamento de listas de um tipo de dados específico.
- System.Exception - Derive classes para definir erros personalizados.
Implementação de 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.
Nota
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 - Esta 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 - Esta interface permite que os usuários comparem instâncias da classe com os
-le
operadores ,-lt
,-ge
e-gt
comparação. Quando uma classe não implementa essa interface, esses operadores geram um erro. - System.IFormattable - Esta interface permite aos usuários formatar instâncias da classe em diferentes strings. 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 - Esta interface permite que os usuários convertam instâncias da classe para 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 de tipo 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 ausing 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 type ao herdar de um genérico.