Partilhar via


Sobre as aulas

Breve descrição

Descreve como pode usar as aulas para criar os seus próprios tipos personalizados.

Longa descrição

PowerShell 5.0 adiciona uma sintaxe formal para definir classes e outros tipos definidos pelo utilizador. A adição de aulas permite que os desenvolvedores e profissionais de TI abracem o PowerShell para uma gama mais ampla de casos de uso. Simplifica o desenvolvimento dos artefactos PowerShell e acelera a cobertura das superfícies de gestão.

Uma declaração de classe é uma planta usada para criar casos de objetos no tempo de execução. Quando se define uma classe, o nome da classe é o nome do tipo. Por exemplo, se declarar uma classe chamada Dispositivo e rubricar uma variável $dev para uma nova instância do Dispositivo, $dev é um objeto ou instância do tipo Dispositivo. Cada instância do Dispositivo pode ter valores diferentes nas suas propriedades.

Cenários suportados

  • Defina tipos personalizados no PowerShell utilizando semânticas familiares de programação orientadas a objetos, como aulas, propriedades, métodos, herança, etc.
  • Tipos de depurg que usam a língua PowerShell.
  • Gerar e manusear exceções utilizando mecanismos formais.
  • Defina os recursos DSC e os seus tipos associados utilizando a linguagem PowerShell.

Syntax

As aulas são declaradas utilizando a seguinte sintaxe:

class <class-name> [: [<base-class>][,<interface-list]] {
    [[<attribute>] [hidden] [static] <property-definition> ...]
    [<class-name>([<constructor-argument-list>])
      {<constructor-statement-list>} ...]
    [[<attribute>] [hidden] [static] <method-definition> ...]
}

As aulas são instantâneas utilizando qualquer uma das seguintes sintaxes:

[$<variable-name> =] New-Object -TypeName <class-name> [
  [-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])

Nota

Ao utilizar a [<class-name>]::new( sintaxe, os suportes em torno do nome da classe são obrigatórios. Os suportes assinalam uma definição de tipo para PowerShell.

Sintaxe de exemplo e utilização

Este exemplo mostra a sintaxe mínima necessária para criar uma classe utilizável.

class Device {
    [string]$Brand
}

$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft

Propriedades de classe

As propriedades são variáveis declaradas no âmbito da classe. Uma propriedade pode ser de qualquer tipo incorporado ou um exemplo de outra classe. As aulas não têm restrições no número de propriedades que têm.

Classe exemplo com propriedades simples

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

$device = [Device]::new()
$device.Brand = "Microsoft"
$device.Model = "Surface Pro 4"
$device.VendorSku = "5072641000"

$device
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Exemplo de tipos complexos em propriedades de classe

Este exemplo define uma classe de cremalheira vazia utilizando a classe Dispositivo. Os exemplos, seguindo este, mostram como adicionar dispositivos à cremalheira e como começar com um rack pré-carregado.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

class Rack {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new(8)

}

$rack = [Rack]::new()

$rack

Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, $null, $null...}


Métodos de classe

Os métodos definem as ações que uma classe pode realizar. Os métodos podem ter parâmetros que fornecem dados de entrada. Os métodos podem devolver a saída. Os dados devolvidos por um método podem ser qualquer tipo de dado definido.

Exemplo de classe simples com propriedades e métodos

Estendendo a classe Rack para adicionar e remover os dispositivos para ou para a partir dele.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    [string]ToString(){
        return ("{0}|{1}|{2}" -f $this.Brand, $this.Model, $this.VendorSku)
    }
}

class Rack {
    [int]$Slots = 8
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void]RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }

    [int[]] GetAvailableSlots(){
        [int]$i = 0
        return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
    }
}

$rack = [Rack]::new()

$surface = [Device]::new()
$surface.Brand = "Microsoft"
$surface.Model = "Surface Pro 4"
$surface.VendorSku = "5072641000"

$rack.AddDevice($surface, 2)

$rack
$rack.GetAvailableSlots()

Slots     : 8
Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, Microsoft|Surface Pro 4|5072641000, $null...}

0
1
3
4
5
6
7

Saída em métodos de classe

Os métodos devem ter um tipo de retorno definido. Se um método não devolver a saída, então o tipo de saída deve ser [void] .

Nos métodos de classe, nenhum objeto é enviado para o oleoduto exceto os mencionados na return declaração. Não há saída acidental para o oleoduto a partir do código.

Nota

Isto é fundamentalmente diferente de como as funções powerShell lidam com a saída, onde tudo vai para o oleoduto.

Saída do método

Este exemplo não demonstra nenhuma saída acidental para o gasoduto a partir de métodos de classe, exceto na return declaração.

class FunWithIntegers
{
    [int[]]$Integers = 0..10

    [int[]]GetOddIntegers(){
        return $this.Integers.Where({ ($_ % 2) })
    }

    [void] GetEvenIntegers(){
        # this following line doesn't go to the pipeline
        $this.Integers.Where({ ($_ % 2) -eq 0})
    }

    [string]SayHello(){
        # this following line doesn't go to the pipeline
        "Good Morning"

        # this line goes to the pipeline
        return "Hello World"
    }
}

$ints = [FunWithIntegers]::new()

$ints.GetOddIntegers()

$ints.GetEvenIntegers()

$ints.SayHello()
1
3
5
7
9
Hello World

Construtor

Os construtores permitem-lhe definir valores predefinidos e validar a lógica do objeto no momento de criar a instância da classe. Os construtores têm o mesmo nome da classe. Os construtores podem ter argumentos para inicializar os membros de dados do novo objeto.

A classe pode ter zero ou mais construtores definidos. Se nenhum construtor for definido, a classe recebe um construtor sem parâmetros padrão. Este construtor inicializa todos os membros aos seus valores predefinidos. Os tipos e cordas dos objetos recebem valores nulos. Quando se define o construtor, não é criado um construtor sem parâmetros padrão. Crie um construtor sem parâmetros, se necessário.

Sintaxe básica do construtor

Neste exemplo, a classe Dispositivo é definida com propriedades e um construtor. Para utilizar esta classe, o utilizador é obrigado a fornecer valores para os parâmetros indicados no construtor.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$surface
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Exemplo com vários construtores

Neste exemplo, a classe Dispositivo é definida com propriedades, um construtor predefinido e um construtor para inicializar a instância.

O construtor padrão define a marca para Undefined, e deixa modelo e fornecedor-sku com valores nulos.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(){
        $this.Brand = 'Undefined'
    }

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$somedevice = [Device]::new()
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$somedevice
$surface
Brand       Model           VendorSku
-----       -----           ---------
Undefined
Microsoft   Surface Pro 4   5072641000

Atributo escondido

O hidden atributo torna uma propriedade ou método menos visível. A propriedade ou método ainda está acessível ao utilizador e está disponível em todos os âmbitos em que o objeto está disponível. Os membros ocultos são escondidos do Get-Member cmdlet e não podem ser exibidos usando a conclusão do separador ou o IntelliSense fora da definição de classe.

Exemplo usando atributos ocultos

Quando um objeto Rack é criado, o número de ranhuras para dispositivos é um valor fixo que não deve ser alterado em nenhum momento. Este valor é conhecido na época da criação.

A utilização do atributo oculto permite ao desenvolvedor manter o número de ranhuras escondidas e evita alterações não intencionais do tamanho da cremalheira.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    [int] hidden $Slots = 8
    [string]$Brand
    [string]$Model
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)
    }
}

[Rack]$r1 = [Rack]::new("Microsoft", "Surface Pro 4", 16)

$r1
$r1.Devices.Length
$r1.Slots
Brand     Model         Devices
-----     -----         -------
Microsoft Surface Pro 4 {$null, $null, $null, $null...}
16
16

Note A propriedade slots não é mostrada na $r1 saída. No entanto, o tamanho foi alterado pelo construtor.

Atributo estático

O static atributo define uma propriedade ou um método que existe na classe e não precisa de instância.

Uma propriedade estática está sempre disponível, independente da instantânea de classe. Uma propriedade estática é partilhada em todos os casos da classe. Um método estático está sempre disponível. Todas as propriedades estáticas vivem durante todo o período de sessão.

Exemplo usando atributos e métodos estáticos

Assuma que as prateleiras instantâneas aqui existem no seu centro de dados. Então, gostaria de acompanhar as prateleiras do seu código.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    hidden [int] $Slots = 8
    static [Rack[]]$InstalledRacks = @()
    [string]$Brand
    [string]$Model
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.AssetId = $id
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)

        ## add rack to installed racks
        [Rack]::InstalledRacks += $this
    }

    static [void]PowerOffRacks(){
        foreach ($rack in [Rack]::InstalledRacks) {
            Write-Warning ("Turning off rack: " + ($rack.AssetId))
        }
    }
}

Testar a propriedade estática e o método existem

PS> [Rack]::InstalledRacks.Length
0

PS> [Rack]::PowerOffRacks()

PS> (1..10) | ForEach-Object {
>>   [Rack]::new("Adatum Corporation", "Standard-16",
>>     $_.ToString("Std0000"), 16)
>> } > $null

PS> [Rack]::InstalledRacks.Length
10

PS> [Rack]::InstalledRacks[3]
Brand              Model       AssetId Devices
-----              -----       ------- -------
Adatum Corporation Standard-16 Std0004 {$null, $null, $null, $null...}

PS> [Rack]::PowerOffRacks()
WARNING: Turning off rack: Std0001
WARNING: Turning off rack: Std0002
WARNING: Turning off rack: Std0003
WARNING: Turning off rack: Std0004
WARNING: Turning off rack: Std0005
WARNING: Turning off rack: Std0006
WARNING: Turning off rack: Std0007
WARNING: Turning off rack: Std0008
WARNING: Turning off rack: Std0009
WARNING: Turning off rack: Std0010

Note que o número de prateleiras aumenta cada vez que corre este exemplo.

Atributos de validação de imóveis

Os atributos de validação permitem testar que os valores dados às propriedades cumprem os requisitos definidos. A validação é desencadeada no momento em que o valor é atribuído. Ver about_functions_advanced_parameters.

Exemplo usando atributos de validação

class Device {
    [ValidateNotNullOrEmpty()][string]$Brand
    [ValidateNotNullOrEmpty()][string]$Model
}

[Device]$dev = [Device]::new()

Write-Output "Testing dev"
$dev

$dev.Brand = ""
Testing dev

Brand Model
----- -----

Exception setting "Brand": "The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again."
At C:\tmp\Untitled-5.ps1:11 char:1
+ $dev.Brand = ""
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

Herança nas classes PowerShell

Pode estender uma classe criando uma nova classe que deriva de uma classe existente. A classe derivada herda as propriedades da classe base. Pode adicionar ou sobrepor métodos e propriedades conforme necessário.

PowerShell não suporta herança múltipla. As aulas não podem herdar de mais de uma classe. No entanto, pode utilizar interfaces para o efeito.

A implementação da herança é definida pelo : operador; o que significa alargar esta classe ou implementar estas interfaces. A classe derivada deve ser sempre a mais à esquerda na declaração de classe.

Exemplo usando sintaxe de herança simples

Este exemplo mostra a simples sintaxe da herança da classe 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 {...}

Exemplo de simples herança nas classes PowerShell

Neste exemplo, as classes rack e dispositivos utilizadas nos exemplos anteriores são mais bem definidas para: evitar repetições de propriedade, alinhar melhor as propriedades comuns e reutilizar a lógica de negócio comum.

A maioria dos objetos no centro de dados são ativos da empresa, o que faz sentido começar a rastreá-los como ativos. Os tipos de dispositivos são definidos pela DeviceType enumeração, ver about_Enum para obter detalhes sobre enumerações.

No nosso exemplo, estamos apenas a definir Rack e a ambas as ComputeServer extensões à Device classe.

enum DeviceType {
    Undefined = 0
    Compute = 1
    Storage = 2
    Networking = 4
    Communications = 8
    Power = 16
    Rack = 32
}

class Asset {
    [string]$Brand
    [string]$Model
}

class Device : Asset {
    hidden [DeviceType]$devtype = [DeviceType]::Undefined
    [string]$Status

    [DeviceType] GetDeviceType(){
        return $this.devtype
    }
}

class ComputeServer : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Compute
    [string]$ProcessorIdentifier
    [string]$Hostname
}

class Rack : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Rack
    hidden [int]$Slots = 8

    [string]$Datacenter
    [string]$Location
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack (){
        ## Just create the default rack with 8 slots
    }

    Rack ([int]$s){
        ## Add argument validation logic here
        $this.Devices = [Device[]]::new($s)
    }

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void] RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }
}

$FirstRack = [Rack]::new(16)
$FirstRack.Status = "Operational"
$FirstRack.Datacenter = "PNW"
$FirstRack.Location = "F03R02.J10"

(0..15).ForEach({
    $ComputeServer = [ComputeServer]::new()
    $ComputeServer.Brand = "Fabrikam, Inc."       ## Inherited from Asset
    $ComputeServer.Model = "Fbk5040"              ## Inherited from Asset
    $ComputeServer.Status = "Installed"           ## Inherited from Device
    $ComputeServer.ProcessorIdentifier = "x64"    ## ComputeServer
    $ComputeServer.Hostname = ("r1s" + $_.ToString("000")) ## ComputeServer
    $FirstRack.AddDevice($ComputeServer, $_)
  })

$FirstRack
$FirstRack.Devices
Datacenter : PNW
Location   : F03R02.J10
Devices    : {r1s000, r1s001, r1s002, r1s003...}
Status     : Operational
Brand      :
Model      :

ProcessorIdentifier : x64
Hostname            : r1s000
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

ProcessorIdentifier : x64
Hostname            : r1s001
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

<... content truncated here for brevity ...>

ProcessorIdentifier : x64
Hostname            : r1s015
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

Chamar construtores de classe base

Para invocar um construtor de classe base a partir de uma subclasse, adicione a base palavra-chave.

class Person {
    [int]$Age

    Person([int]$a)
    {
        $this.Age = $a
    }
}

class Child : Person
{
    [string]$School

    Child([int]$a, [string]$s ) : base($a) {
        $this.School = $s
    }
}

[Child]$littleone = [Child]::new(10, "Silver Fir Elementary School")

$littleone.Age

10

Invocar métodos de classe base

Para anular os métodos existentes em subclasses, declare métodos utilizando o mesmo nome e assinatura.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
}

[ChildClass1]::new().days()

2

Para chamar os métodos de classe base de implementações superadas, lançados para a classe base ([baseclass]$this) na invocação.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
    [int]basedays() {return ([BaseClass]$this).days()}
}

[ChildClass1]::new().days()
[ChildClass1]::new().basedays()

2
1

Interfaces

A sintaxe para declarar interfaces é semelhante a C#. Pode declarar interfaces após tipos de base ou imediatamente após um cólon : quando não existe um tipo de base especificado. Separe todos os nomes do tipo com vírgulas.

class MyComparable : system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

class MyComparableBar : bar, system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

Aulas de importação de um módulo PowerShell

Import-Modulee a #requires afirmação apenas importa as funções do módulo, pseudónimos e variáveis, tal como definido pelo módulo. As aulas não são importadas. A using module declaração importa as classes definidas no módulo. Se o módulo não estiver carregado na sessão atual, a using declaração falha.

Ver também