Acerca de las clases
Descripción breve
Describe cómo puede usar clases para crear sus propios tipos personalizados.
Descripción larga
PowerShell 5.0 agrega una sintaxis formal para definir clases y otros tipos definidos por el usuario. La adición de clases permite a los desarrolladores y profesionales de TI adoptar PowerShell para una gama más amplia de casos de uso. Simplifica el desarrollo de artefactos de PowerShell y acelera la cobertura de superficies de administración.
Una declaración de clase es un plano técnico que se usa para crear instancias de objetos en tiempo de ejecución. Al definir una clase, el nombre de la clase es el nombre del tipo. Por ejemplo, si declara una clase denominada Device e inicializa una variable $dev
en una nueva instancia de Device, $dev
es un objeto o instancia de tipo Device. Cada instancia de Device puede tener valores diferentes en sus propiedades.
Escenarios admitidos
- Defina tipos personalizados en PowerShell con una semántica de programación familiar orientada a objetos, como clases, propiedades, métodos, herencia, etc.
- Depurar tipos mediante el lenguaje de PowerShell.
- Genere y controle excepciones mediante mecanismos formales.
- Defina los recursos de DSC y sus tipos asociados mediante el lenguaje de PowerShell.
Syntax
Las clases se declaran mediante la sintaxis siguiente:
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> ...]
}
Se crean instancias de clases mediante cualquiera de las sintaxis siguientes:
[$<variable-name> =] New-Object -TypeName <class-name> [
[-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])
Nota
Al usar la [<class-name>]::new(
sintaxis, los corchetes alrededor del nombre de clase son obligatorios. Los corchetes indican una definición de tipo para PowerShell.
Ejemplo de sintaxis y uso
En este ejemplo se muestra la sintaxis mínima necesaria para crear una clase utilizable.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft
Propiedades de clase
Las propiedades son variables declaradas en el ámbito de clase. Una propiedad puede ser de cualquier tipo integrado o de una instancia de otra clase. Las clases no tienen ninguna restricción en el número de propiedades que tienen.
Clase de ejemplo con propiedades 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
Tipos complejos de ejemplo en las propiedades de clase
En este ejemplo se define una clase Rack vacía mediante la clase Device . En los ejemplos siguientes se muestra cómo agregar dispositivos al bastidor y cómo empezar con un bastidor cargado previamente.
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 clase
Los métodos definen las acciones que una clase puede realizar. Los métodos pueden tomar parámetros que proporcionan datos de entrada. Los métodos pueden devolver la salida. Los datos devueltos por un método pueden ser cualquier tipo de datos definido.
Ejemplo de clase simple con propiedades y métodos
Extender la clase Rack para agregar y quitar dispositivos hacia o desde ella.
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
Salida en métodos de clase
Los métodos deben tener definido un tipo de valor devuelto. Si un método no devuelve la salida, el tipo de salida debe ser [void]
.
En los métodos de clase, no se envían objetos a la canalización excepto los mencionados en la return
instrucción . No hay ninguna salida accidental en la canalización desde el código.
Nota
Esto es fundamentalmente diferente de cómo las funciones de PowerShell controlan la salida, donde todo va a la canalización.
Salida del método
En este ejemplo se muestra ninguna salida accidental a la canalización de métodos de clase, excepto en la return
instrucción .
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
Constructor
Los constructores permiten establecer valores predeterminados y validar la lógica de objetos en el momento de crear la instancia de la clase. Los constructores tienen el mismo nombre que la clase . Los constructores pueden tener argumentos para inicializar los miembros de datos del nuevo objeto.
La clase puede tener cero o más constructores definidos. Si no se define ningún constructor, la clase recibe un constructor sin parámetros predeterminado. Este constructor inicializa todos los miembros en sus valores predeterminados. Los tipos de objeto y las cadenas tienen valores NULL. Al definir el constructor, no se crea ningún constructor sin parámetros predeterminado. Create un constructor sin parámetros si es necesario.
Sintaxis básica del constructor
En este ejemplo, la clase Device se define con propiedades y un constructor. Para usar esta clase, el usuario es necesario para proporcionar valores para los parámetros enumerados en el constructor.
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
Ejemplo con varios constructores
En este ejemplo, la clase Device se define con propiedades, un constructor predeterminado y un constructor para inicializar la instancia.
El constructor predeterminado establece la marca en Undefined y deja el modelo y la SKU del proveedor con valores NULL.
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 oculto
El hidden
atributo hace que una propiedad o un método sean menos visibles. La propiedad o el método siguen siendo accesibles para el usuario y están disponibles en todos los ámbitos en los que el objeto está disponible. Los miembros ocultos están ocultos del Get-Member
cmdlet y no se pueden mostrar mediante la finalización de tabulación o IntelliSense fuera de la definición de clase.
Ejemplo de uso de atributos ocultos
Cuando se crea un objeto Rack , el número de ranuras para dispositivos es un valor fijo que no se debe cambiar en ningún momento. Este valor se conoce en el momento de la creación.
El uso del atributo oculto permite al desarrollador mantener el número de ranuras ocultas y evita cambios involuntarios en el tamaño del bastidor.
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
Observe que la propiedad Slots no se muestra en $r1
la salida. Sin embargo, el constructor cambió el tamaño.
Atributo estático
El static
atributo define una propiedad o un método que existe en la clase y no necesita ninguna instancia.
Una propiedad estática siempre está disponible, independientemente de la creación de instancias de clase. Una propiedad estática se comparte en todas las instancias de la clase . Un método estático siempre está disponible. Todas las propiedades estáticas residen para todo el intervalo de sesión.
Ejemplo de uso de atributos y métodos estáticos
Supongamos que los bastidores creados aquí existen en el centro de datos. Por lo tanto, le gustaría realizar un seguimiento de los bastidores en el 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))
}
}
}
Existen métodos y propiedades estáticas de prueba
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
Observe que el número de bastidores aumenta cada vez que se ejecuta este ejemplo.
Atributos de validación de propiedades
Los atributos de validación permiten probar que los valores proporcionados a las propiedades cumplen los requisitos definidos. La validación se desencadena en el momento en que se asigna el valor. Consulte about_functions_advanced_parameters.
Ejemplo de uso de atributos de validación
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
Herencia en clases de PowerShell
Puede ampliar una clase mediante la creación de una nueva clase que derive de una clase existente. La clase derivada hereda las propiedades de la clase base. Puede agregar o invalidar métodos y propiedades según sea necesario.
PowerShell no admite varias herencias. Las clases no pueden heredar de más de una clase. Sin embargo, puede usar interfaces para ese propósito.
La implementación de herencia se define mediante el :
operador , lo que significa extender esta clase o implementar estas interfaces. La clase derivada siempre debe estar más a la izquierda en la declaración de clase.
Ejemplo de uso de la sintaxis de herencia simple
En este ejemplo se muestra la sintaxis simple de herencia de clases de PowerShell.
Class Derived : Base {...}
En este ejemplo se muestra la herencia con una declaración de interfaz que viene después de la clase base.
Class Derived : Base.Interface {...}
Ejemplo de herencia simple en clases de PowerShell
En este ejemplo, las clases Rack y Device usadas en los ejemplos anteriores están mejor definidas para: evitar repeticiones de propiedades, alinear mejor las propiedades comunes y reutilizar la lógica de negocios común.
La mayoría de los objetos del centro de datos son activos de la empresa, lo que tiene sentido empezar a realizar un seguimiento de ellos como activos. Los tipos de dispositivo se definen mediante la DeviceType
enumeración, consulte about_Enum para obtener más información sobre las enumeraciones.
En nuestro ejemplo, solo Rack
definimos y ComputeServer
; ambas extensiones a la Device
clase .
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
Llamar a constructores de clase base
Para invocar un constructor de clase base desde una subclase, agregue la base
palabra clave .
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 clase base
Para invalidar los métodos existentes en subclases, declare los métodos con el mismo nombre y la misma firma.
class BaseClass
{
[int]days() {return 1}
}
class ChildClass1 : BaseClass
{
[int]days () {return 2}
}
[ChildClass1]::new().days()
2
Para llamar a métodos de clase base desde implementaciones invaliddas, convierta a la clase base ([baseclass]$this) en la invocación.
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
La sintaxis para declarar interfaces es similar a C#. Puede declarar interfaces después de los tipos base o inmediatamente después de dos puntos (:
) cuando no se especifica ningún tipo base. Separe todos los nombres de tipo con comas.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableBar : bar, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Importación de clases desde un módulo de PowerShell
Import-Module
y la #requires
instrucción solo importan las funciones, alias y variables del módulo, tal como se define en el módulo. Las clases no se importan. La using module
instrucción importa las clases definidas en el módulo. Si el módulo no se carga en la sesión actual, se produce un error en la using
instrucción .