Acerca de los constructores de clases.
Breve descripción
Describe cómo definir constructores para clases de PowerShell.
Descripción larga
Los constructores le permiten establecer valores predeterminados y validar la lógica del objeto al momento de crear la instancia de la clase. Los constructores tienen el mismo nombre que la clase. Los constructores pueden tener parámetros para inicializar los miembros de datos del nuevo objeto.
Los constructores de clases de PowerShell se definen como métodos especiales en la clase. Se comportan igual que los métodos de clase de PowerShell con las siguientes excepciones:
- Los constructores no tienen un tipo de salida. No pueden utilizar la palabra clave
return. - Los constructores siempre tienen el mismo nombre que la clase.
- No se puede llamar a los constructores directamente. Solo se ejecutan cuando se crea una instancia.
- Los constructores nunca aparecen en la salida del cmdlet
Get-Member.
Para obtener más información sobre los métodos de clase de PowerShell, consulte about_Classes_Methods.
La clase puede tener cero o más constructores definidos. Si no se define ningún constructor, la clase recibe un constructor predeterminado sin parámetros. Este constructor inicializa todos los miembros con sus valores predeterminados. Los tipos de objetos y cadenas reciben valores nulos. Cuando define el constructor, no se crea ningún constructor predeterminado sin parámetros. Cree un constructor sin parámetros si es necesario.
También puede definir un constructor estático sin parámetros.
Sintaxis
Los constructores de clases utilizan las siguientes sintaxis:
Sintaxis de constructor predeterminada
<class-name> () [: base([<params>])] {
<body>
}
Sintaxis del constructor estático
static <class-name> () [: base([<params>])] {
<body>
}
Sintaxis del constructor parametrizado (unifilar)
<class-name> ([[<parameter-type>]$<parameter-name>[, [<parameter-type>]$<parameter-name>...]]) [: base([<params>])] {
<body>
}
Sintaxis del constructor parametrizado (multilínea)
<class-name> (
[<parameter-type>]$<parameter-name>[,
[<parameter-type>]$<parameter-name>...]
) [: base([<params>])] {
<body>
}
Ejemplos
Ejemplo 1: definición de una clase con el constructor predeterminado
La clase ExampleBook1 no define un constructor. En su lugar, utiliza el constructor automático predeterminado.
class ExampleBook1 {
[string] $Name
[string] $Author
[int] $Pages
[datetime] $PublishedOn
}
[ExampleBook1]::new()
Name Author Pages PublishedOn
---- ------ ----- -----------
0 1/1/0001 12:00:00 AM
Nota
El valor predeterminado para las propiedades Nombre y Autor es $null porque se escriben como cadenas, que es un tipo de referencia. Las otras propiedades tienen el valor predeterminado para su tipo definido, porque son propiedades de tipo valor. Para obtener más información sobre los valores predeterminados de las propiedades, consulte "Valores de propiedad predeterminados" en about_Classes_Properties.
Ejemplo 2: anulación del constructor predeterminado
ExampleBook2 define explícitamente el constructor predeterminado, estableciendo los valores para PublishedOn en la fecha actual y Pages en 1.
class ExampleBook2 {
[string] $Name
[string] $Author
[int] $Pages
[datetime] $PublishedOn
ExampleBook2() {
$this.PublishedOn = (Get-Date).Date
$this.Pages = 1
}
}
[ExampleBook2]::new()
Name Author Pages PublishedOn
---- ------ ----- -----------
1 11/1/2023 12:00:00 AM
Ejemplo 3: definición de sobrecargas de constructor
La clase ExampleBook3 define tres sobrecargas de constructor, lo que permite a los usuarios crear una instancia de la clase a partir de una tabla hash, pasando cada valor de propiedad y pasando el nombre del libro y el autor. La clase no define el constructor predeterminado.
class ExampleBook3 {
[string] $Name
[string] $Author
[int] $Pages
[datetime] $PublishedOn
ExampleBook3([hashtable]$Info) {
switch ($Info.Keys) {
'Name' { $this.Name = $Info.Name }
'Author' { $this.Author = $Info.Author }
'Pages' { $this.Pages = $Info.Pages }
'PublishedOn' { $this.PublishedOn = $Info.PublishedOn }
}
}
ExampleBook3(
[string] $Name,
[string] $Author,
[int] $Pages,
[datetime] $PublishedOn
) {
$this.Name = $Name
$this.Author = $Author
$this.Pages = $Pages
$this.PublishedOn = $PublishedOn
}
ExampleBook3([string]$Name, [string]$Author) {
$this.Name = $Name
$this.Author = $Author
}
}
[ExampleBook3]::new(@{
Name = 'The Hobbit'
Author = 'J.R.R. Tolkien'
Pages = 310
PublishedOn = '1937-09-21'
})
[ExampleBook3]::new('The Hobbit', 'J.R.R. Tolkien', 310, '1937-09-21')
[ExampleBook3]::new('The Hobbit', 'J.R.R. Tolkien')
[ExampleBook3]::new()
Name Author Pages PublishedOn
---- ------ ----- -----------
The Hobbit J.R.R. Tolkien 310 9/21/1937 12:00:00 AM
The Hobbit J.R.R. Tolkien 310 9/21/1937 12:00:00 AM
The Hobbit J.R.R. Tolkien 0 1/1/0001 12:00:00 AM
MethodException:
Line |
42 | [ExampleBook3]::new()
| ~~~~~~~~~~~~~~~~~~~~~
| Cannot find an overload for "new" and the argument count: "0".
Llamar al constructor predeterminado devuelve una excepción de método. El constructor automático predeterminado solo se define para una clase cuando la clase no define ningún constructor. Debido a que ExampleBook3 define múltiples sobrecargas, el constructor predeterminado no se agrega automáticamente a la clase.
Ejemplo 4: encadenamiento de constructores con un método compartido
Este ejemplo muestra cómo se puede escribir código compartido reutilizable para constructores. Las clases de PowerShell no pueden usar el encadenamiento de constructores, por lo que esta clase de ejemplo define un método Init(). El método tiene varias sobrecargas. Las sobrecargas con menos parámetros llaman a sobrecargas más explícitas con valores predeterminados para los parámetros no especificados.
class ExampleBook4 {
[string] $Name
[string] $Author
[datetime] $PublishedOn
[int] $Pages
ExampleBook4() {
$this.Init()
}
ExampleBook4([string]$Name) {
$this.Init($Name)
}
ExampleBook4([string]$Name, [string]$Author) {
$this.Init($Name, $Author)
}
ExampleBook4([string]$Name, [string]$Author, [datetime]$PublishedOn) {
$this.Init($Name, $Author, $PublishedOn)
}
ExampleBook4(
[string]$Name,
[string]$Author,
[datetime]$PublishedOn,
[int]$Pages
) {
$this.Init($Name, $Author, $PublishedOn, $Pages)
}
hidden Init() {
$this.Init('Unknown')
}
hidden Init([string]$Name) {
$this.Init($Name, 'Unknown')
}
hidden Init([string]$Name, [string]$Author) {
$this.Init($Name, $Author, (Get-Date).Date)
}
hidden Init([string]$Name, [string]$Author, [datetime]$PublishedOn) {
$this.Init($Name, $Author, $PublishedOn, 1)
}
hidden Init(
[string]$Name,
[string]$Author,
[datetime]$PublishedOn,
[int]$Pages
) {
$this.Name = $Name
$this.Author = $Author
$this.PublishedOn = $PublishedOn
$this.Pages = $Pages
}
}
[ExampleBook4]::new()
[ExampleBook4]::new('The Hobbit')
[ExampleBook4]::new('The Hobbit', 'J.R.R. Tolkien')
[ExampleBook4]::new('The Hobbit', 'J.R.R. Tolkien', (Get-Date '1937-9-21'))
[ExampleBook4]::new(
'The Hobbit',
'J.R.R. Tolkien',
(Get-Date '1937-9-21'),
310
)
Name Author PublishedOn Pages
---- ------ ----------- -----
Unknown Unknown 11/1/2023 12:00:00 AM 1
The Hobbit Unknown 11/1/2023 12:00:00 AM 1
The Hobbit J.R.R. Tolkien 11/1/2023 12:00:00 AM 1
The Hobbit J.R.R. Tolkien 9/21/1937 12:00:00 AM 1
The Hobbit J.R.R. Tolkien 9/21/1937 12:00:00 AM 310
Ejemplo 5: constructores de clases derivadas
Los siguientes ejemplos utilizan clases que definen los constructores estáticos, predeterminados y parametrizados para una clase base y una clase derivada que hereda de la clase base.
class BaseExample {
static [void] DefaultMessage([type]$Type) {
Write-Verbose "[$($Type.Name)] default constructor"
}
static [void] StaticMessage([type]$Type) {
Write-Verbose "[$($Type.Name)] static constructor"
}
static [void] ParamMessage([type]$Type, [object]$Value) {
Write-Verbose "[$($Type.Name)] param constructor ($Value)"
}
static BaseExample() { [BaseExample]::StaticMessage([BaseExample]) }
BaseExample() { [BaseExample]::DefaultMessage([BaseExample]) }
BaseExample($Value) { [BaseExample]::ParamMessage([BaseExample], $Value) }
}
class DerivedExample : BaseExample {
static DerivedExample() { [BaseExample]::StaticMessage([DerivedExample]) }
DerivedExample() { [BaseExample]::DefaultMessage([DerivedExample]) }
DerivedExample([int]$Number) : base($Number) {
[BaseExample]::ParamMessage([DerivedExample], $Number)
}
DerivedExample([string]$String) {
[BaseExample]::ParamMessage([DerivedExample], $String)
}
}
El siguiente bloque muestra los mensajes detallados para llamar a los constructores de la clase base. El mensaje del constructor estático solo se emite la primera vez que se crea una instancia de la clase.
PS> $VerbosePreference = 'Continue'
PS> $b = [BaseExample]::new()
VERBOSE: [BaseExample] static constructor
VERBOSE: [BaseExample] default constructor
PS> $b = [BaseExample]::new()
VERBOSE: [BaseExample] default constructor
PS> $b = [BaseExample]::new(1)
VERBOSE: [BaseExample] param constructor (1)
El siguiente bloque muestra los mensajes detallados para llamar a los constructores de clases derivadas en una nueva sesión. La primera vez que se llama a un constructor de clase derivada, se llaman los constructores estáticos para la clase base y la clase derivada. Esos constructores no se vuelven a llamar en la sesión. Los constructores de la clase base siempre se ejecutan antes que los constructores de la clase derivada.
PS> $VerbosePreference = 'Continue'
PS> $c = [DerivedExample]::new()
VERBOSE: [BaseExample] static constructor
VERBOSE: [DerivedExample] static constructor
VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] default constructor
PS> $c = [DerivedExample]::new()
VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] default constructor
PS> $c = [DerivedExample]::new(1)
VERBOSE: [BaseExample] param constructor (1)
VERBOSE: [DerivedExample] param constructor (1)
PS> $c = [DerivedExample]::new('foo')
VERBOSE: [BaseExample] default constructor
VERBOSE: [DerivedExample] param constructor (foo)
Orden de ejecución del constructor
Cuando se crea una instancia de una clase, se ejecuta el código de uno o más constructores.
Para las clases que no heredan de otra clase, el orden es:
- El constructor estático de la clase.
- La sobrecarga del constructor aplicable para la clase.
Para clases derivadas que heredan de otra clase, el orden es:
- El constructor estático de la clase base.
- El constructor estático de la clase derivada.
- Si el constructor de la clase derivada llama explícitamente a una sobrecarga del constructor base, ejecuta ese constructor para la clase base. Si no llama explícitamente a un constructor base, ejecuta el constructor predeterminado para la clase base.
- La sobrecarga del constructor aplicable para la clase derivada.
En todos los casos, los constructores estáticos sólo se ejecutan una vez por sesión.
Para ver un ejemplo de comportamiento y orden del constructor, consulte el Ejemplo 5.
Constructores ocultos
Puede ocultar los constructores de una clase declarándolos con la palabra clave hidden. Los constructores de clases ocultas son:
- No incluido en la salida predeterminada de la clase.
- No incluido en la lista de miembros de la clase devuelta por el cmdlet
Get-Member. Para mostrar propiedades ocultas conGet-Member, utilice el parámetro Force. - No se muestra al completar con pestañas o IntelliSense a menos que la finalización se produzca en la clase que define la propiedad oculta.
- Miembros públicos de la clase. Se puede acceder a ellos y modificarlos. Ocultar una propiedad no la convierte en privada. Sólo oculta la propiedad como se describe en los puntos anteriores.
Nota
Cuando oculta cualquier constructor, la opción new() se elimina de IntelliSense y de los resultados de finalización.
Para obtener más información sobre la palabra clave hidden, consulte about_Hidden.
Constructores estáticos
Puede definir un constructor como perteneciente a la clase misma en lugar de instancias de la clase declarando el constructor con la palabra clave static. Constructores de clases estáticas:
- Invocar solo la primera vez que se crea una instancia de la clase en la sesión.
- No puede tener ningún parámetro.
- No se puede acceder a las propiedades o métodos de la instancia con la variable
$this.
Constructores para clases derivadas
Cuando una clase hereda de otra clase, los constructores pueden invocar un constructor de la clase base con la palabra clave base. Si la clase derivada no invoca explícitamente un constructor de la clase base, en su lugar invoca el constructor predeterminado para la clase base.
Para invocar un constructor base no predeterminado, agregue : base( después de los parámetros del constructor y antes del bloque de cuerpo.
class <derived-class> : <base-class> {
<derived-class>(<derived-parameters>) : <base-class>(<base-parameters>) {
# initialization code
}
}
Al definir un constructor que llama a un constructor de clase base, los parámetros pueden ser cualquiera de los siguientes elementos:
- La variable de cualquier parámetro en el constructor de la clase derivada.
- Cualquier valor estático.
- Cualquier expresión que se evalúe como un valor del tipo de parámetro.
Para ver un ejemplo de constructores en una clase derivada, consulte el Ejemplo 5.
Constructores encadenados
A diferencia de C#, los constructores de clases de PowerShell no pueden usar el encadenamiento con la sintaxis : this(. Para reducir la duplicación de código, utilice un método oculto Init() con múltiples sobrecargas para el mismo efecto. El ejemplo 4 muestra una clase que usa este patrón.
Agregar propiedades y métodos de instancia con Update-TypeData
Además de declarar propiedades y métodos directamente en la definición de clase, puede definir propiedades para instancias de una clase en el constructor estático mediante el cmdlet Update-TypeData.
Utilice este fragmento como punto de partida para el patrón. Reemplace el texto del marcador de posición entre corchetes angulares según sea necesario.
class <class-name> {
static [hashtable[]] $MemberDefinitions = @(
@{
Name = '<member-name>'
MemberType = '<member-type>'
Value = <member-definition>
}
)
static <class-name>() {
$TypeName = [<class-name>].Name
foreach ($Definition in [<class-name>]::MemberDefinitions) {
Update-TypeData -TypeName $TypeName @Definition
}
}
}
Consejo útil
El cmdlet Add-Member puede agregar propiedades y métodos a una clase en constructores no estáticos, pero el cmdlet se ejecuta cada vez que se llama al constructor. El uso de Update-TypeData en el constructor estático garantiza que el código para agregar miembros a la clase solo deba ejecutarse una vez en una sesión.
Solo agregue propiedades a la clase en constructores no estáticos cuando no se puedan definir con Update-TypeData, como propiedades de solo lectura.
Para obtener más información sobre cómo definir métodos de instancia con Update-TypeData, consulte about_Classes_Methods. Para obtener más información sobre cómo definir propiedades de instancia con Update-TypeData, consulte about_Classes_Properties.
Limitaciones
Los constructores de clases de PowerShell tienen las siguientes limitaciones:
El encadenamiento de constructores no está implementado.
Solución alternativa: defina métodos
Init()ocultos y llámelos desde los constructores.Los parámetros del constructor no pueden usar ningún atributo, incluidos los atributos de validación.
Solución alternativa: reasigne los parámetros en el cuerpo del constructor con el atributo de validación.
Los parámetros del constructor no pueden definir valores predeterminados. Los parámetros son siempre obligatorios.
Solución alternativa: ninguna.
Si alguna sobrecarga de un constructor está oculta, cada sobrecarga del constructor también se trata como oculta.
Solución alternativa: ninguna.
Ver también
- acerca de_clases
- about_Classes_Herencia
- acerca de_clases_métodos
- about_Classes_Properties