Saltar al contenido

Tipos de matriz en Powershell – System.Object[] frente a matrices con tipos específicos

Hola, tenemos la solución a lo que necesitas, desplázate y la verás más abajo.

Solución:

Punta del sombrero a PetSerAl por toda su ayuda.

Para complementar la útil respuesta de Miroslav Adamec con por qué PowerShell crea System.Object[] matrices por defecto e información adicional de antecedentes:

Las matrices predeterminadas de PowerShell están destinadas a ser flexible:

  • te permiten almacenar objetos de cualquier tipo (incluso $null),
  • incluso permitiéndote mezcla objetos de diferentes tipos en un solo array.

Para habilitar esto, el array debe escribirse (implícitamente) como [object[]] ([System.Object[]]), porque System.Object es la raíz única de toda la jerarquía de tipos .NET de la que derivan todos los demás tipos.

Por ejemplo, lo siguiente crea una [object[]] array cuyos elementos son de tipo [string], [int], [datetime], y $null, respectivamente.

$arr = 'hi', 42, (Get-Date), $null  # @(...) is not needed; `, ` for a 1-elem. arr.

Cuando usted:

  • crear un array usando el array construcción operador, ,

  • forzar la salida del comando en un array usando el array subexpresión operador, @(...)

  • guardar en una variable los salida de un comando que emite a colección de objetos con 2 o más elementos, independientemente del tipo específico de colección original, u operar en él en el contexto de otro comando por encerrándolo en (...)

usted siempre obtener una System.Object[] array – incluso si todos los elementos suceder a tienen el mismo tipo, como en su ejemplo.


Lectura adicional opcional

De PowerShell las matrices predeterminadas son convenientes, pero tienen inconvenientes:

  • Ellos proveen sin tipo de seguridad: si desea asegurarse de que todos los elementos sean de un tipo específico (o deberían convertirse a él, si es posible), un array no servirá; p.ej:

      $intArray = 1, 2      # An array of [int] values.
      $intArray[0] = 'one'  # !! Works, because a [System.Object[]] array can hold any type.
    
  • [System.Object[]] las matrices son ineficaz para tipos de valor tal como [int], porque boxeo y desembalaje debe realizarse, aunque a menudo eso no importa en el mundo real.

Dado que PowerShell proporciona acceso al sistema de tipos .NET, puede evitar los inconvenientes si crear un array que está restringido al tipo específico de interés, utilizando un emitir o variable de tipo restringido:

[int[]] $intArray = 1, 2  # A type-constrained array of [int] variable.
$intArray[0] = 'one'      # BREAKS: 'one' can't be converted to an [int]

Tenga en cuenta que el uso de emitir para crear el array – $intArray = [int[]] (1, 2) – también habría funcionado, pero solo la variable con restricción de tipo asegura que no puede más tarde asignar un valor de un tipo diferente al variable (p.ej, $intArray = 'one', 'two' fallaría).

Escollo de sintaxis con yesos: [int[]] 1, 2 lo hace no funcionan según lo previsto, porque las conversiones tienen una alta precedencia de operadores, por lo que la expresión se evalúa como ([int[]] 1), 2, que crea una regular [object[]] array cuyo primero elemento es un anidado[int[]] array con un solo elemento 1.
En caso de duda, utilice @(...) alrededor de tu array elementos[1], que también es necesario si desea asegurarse de que una expresión que puede devolver solo un soltero El artículo siempre se trata como un array.


Trampas

PowerShell realiza muchas conversiones de tipos entre bastidores, que suelen ser muy útiles, pero hay trampas:

  • Potencia Shell intenta automáticamente coaccionar un valor a un tipo de objetivo, que no siempre desea y puede que no note:

      [string[]] $a = 'one', 'two'
      $a[0] = 1    # [int] 1 is quietly coerced to [string]
    
      # The coercion happens even if you use a cast:
      [string[]] $a = 'one', 'two'
      $a[0] = [int] 1    # Quiet coercion to [string] still happens.
    

    Nota: que incluso un elenco explícito … [int] 1 – Causa una coacción silenciosa que puede o no ser una sorpresa para ti. Mi sorpresa vino de, incorrectamente, suponer que en un lenguaje de coerción automática como los casts de PowerShell podría ser una forma de derivación la coerción – que es no true.[2]

    Dado que alguna el tipo se puede convertir en un string, a [string[]] array es el caso más complicado.
    usted hacer obtener un error si no se puede realizar la coerción (automática), como con
    [int[]] $arr = 1, 2; $arr[0] = 'one' # error

  • “Agregar a” un tipo específico array crea un nuevo array de tipo [object[]]:

    PowerShell convenientemente le permite “agregar a” arreglos con el + operador.
    En realidad, a nuevo array es creado detrás de escena con los elementos adicionales añadidos, pero que nuevo array es por defecto de nuevo de tipo [object[]], independientemente del tipo de entrada array:

      $intArray = [int[]] (1, 2)
      ($intArray + 4).GetType().Name # !! -> 'Object[]'
      $intArray += 3 # !! $intArray is now of type [object[]]
    
      # To avoid the problem...
      # ... use casting:
      ([int[]] ($intArray + 4)).GetType().Name # -> 'Int32[]'
      # ... or use a type-constrained variable:
      [int[]] $intArray = (1, 2) # a type-constrained variable
      $intArray += 3 # still of type [int[]], due to type constraint.
    
  • La salida al flujo de éxito convierte cualquier colección a [object[]]:

    Cualquier colección con al menos 2 elementos que un comando o canalización genera (al flujo de éxito) es convertido automáticamente en un array de tipo [object[]], que puede ser inesperado:

      # A specifically-typed array:
      # Note that whether or not `return` is used makes no difference.
      function foo  return [int[]] (1, 2) 
      # Important: foo inside (...) is a *command*, not an *expression*
      # and therefore a *pipeline* (of length 1)
      (foo).GetType().Name # !! -> 'Object[]'
    
      # A different collection type:
      function foo  return [System.Collections.ArrayList] (1, 2) 
      (foo).GetType().Name # !! -> 'Object[]'
    
      # Ditto with a multi-segment pipeline:
      ([System.Collections.ArrayList] (1, 2) | Write-Output).GetType().Name # !! -> 'Object[]'
    

    La razón de este comportamiento es que PowerShell es fundamentalmente basado en colecciones: se envía la salida de cualquier comando punto por punto a través de la tubería; tenga en cuenta que incluso un soltero El comando es una tubería (de longitud 1).

    Es decir, PowerShell siempre primero desenvuelve colecciones, y luego, si es necesario, vuelve a montar ellos – por asignación a un variable, o como el resultado intermedio de un comando anidado dentro (...) – y el la colección reensamblada es siempre de tipo [object[]].

    PowerShell considera un objeto una colección si su tipo implementa el IEnumerable interfaz, excepto si también implementa el IDictionary interfaz.
    Esta excepción significa que las tablas hash de PowerShell ([hashtable]) y tablas hash ordenadas (la variante literal de PSv3 + con ordenadas keys, [ordered] @..., que es de tipo [System.Collections.Specialized.OrderedDictionary]) se envían a través de la tubería como un todo, y en su lugar enumerar sus entradas (keypares de valores) individualmente, debe llamar a sus .GetEnumerator() método.

  • PowerShell por diseño siempre desenvuelve a soltero– colección de salida de elementos a ese único elemento:

    En otras palabras: cuando se genera una colección de un solo elemento, PowerShell no devuelve un array, pero el arrayelemento único de sí mismo.

      # The examples use single-element array ,1 
      # constructed with the unary form of array-construction operator ","
      # (Alternatively, @( 1 ) could be used in this case.)
    
      # Function call:
      function foo  ,1 
      (foo).GetType().Name # -> 'Int32'; single-element array was *unwrapped*
    
      # Pipeline:
      ( ,1 | Write-Output ).GetType().Name # -> 'Int32'
    
      # To force an expression into an array, use @(...):
      @( (,1) | Write-Output ).GetType().Name # -> 'Object[]' - result is array
    

    Hablando libremente, el propósito de array operador de subexpresión @(...) es: Trate siempre el valor adjunto como un colección, incluso si contiene (o normalmente se desenvolvería) solo una objeto unico:
    Si es un soltero valor, envuélvalo en [object[]] array con 1 elemento.
    Los valores que ya son colecciones siguen siendo colecciones, aunque son convertido en un nuevo [object[]] array, incluso si el valor en sí ya es un array:
    $a1 = 1, 2; $a2 = @( $a1 ); [object]::ReferenceEquals($a1, $a2)

    salidas $false, demostrando que las matrices $a1 y $a2 no son lo mismo.

    Contraste esto con:

    • solo (...), cuales lo hace no per se cambiar el tipo de valor – su propósito es simplemente aclarar la precedencia o forzar un nuevo contexto de análisis:

      • Si el adjunto la construcción es una expresión (algo analizado en modo de expresión), el tipo es no cambió; p.ej, ([System.Collections.ArrayList] (1, 2)) -is [System.Collections.ArrayList] y ([int[]] (1,2)) -is [int[]] ambos regresan $true – se conserva el tipo.

      • Si el adjunto la construcción es una mando (uno o varios segmentos tubería), entonces el se aplica el comportamiento de desenvolvimiento predeterminado; p.ej:
        (& , 1 ) -is [int] devoluciones $true (el elemento único array fue desenvuelto) y (& [int[]] (1, 2) ) -is [object[]] (los [int[]] array fue reensamblado en un [object[]] array) ambos regresan $true, porque el uso del operador de llamada & hizo de la construcción adjunta un mando.

    • (regular) operador de subexpresión $(...), normalmente utilizado en cadenas expandibles, que exhibe el comportamiento de desenvolvimiento predeterminado:
      $(,1) -is [int] y $([System.Collections.ArrayList] (1, 2)) -is [object[]] ambos regresan $true.

  • Devolver una colección como un todo desde una función o script:

    En ocasiones, es posible que desee publicar una colección como un todo, es decir, para generarlo como un soltero artículo, conservando su tipo original.

    Como hemos visto anteriormente, la salida de una colección tal como está hace que PowerShell la desenvuelva y, en última instancia, la vuelva a ensamblar en un formato normal. [object[]] array.

    Para evitar eso, el unario forma de array operador de construccion , puede ser usado para envolver la colección en un exterior array, que PowerShell luego desenvuelve en la colección original:

      # Wrap array list in regular array with leading ","
      function foo  , [System.Collections.ArrayList] (1, 2) 
      # The call to foo unwraps the outer array and assigns the original
      # array list to $arrayList.
      $arrayList = foo
      # Test
      $arrayList.GetType().Name # -> 'ArrayList'
    

    En PSv4 +, usar Write-Output -NoEnumerate:

      function foo  write-output -NoEnumerate ([System.Collections.ArrayList] (1, 2)) 
      $arrayList = foo
      $arrayList.GetType().Name # -> 'ArrayList'
    

[1] Tenga en cuenta que utilizando @(...) crear array literales no es necesario, porque el array-operador de construcción ,solo crea matrices.
En versiones anteriores a PSv5.1, también paga una penalización de rendimiento (en la mayoría de los casos probablemente insignificante), porque el ,-construido array dentro @() es efectivamente clonado por @() – vea esta respuesta mía para más detalles.
Dicho eso @(...) tiene ventajas:

  • Puede utilizar la misma sintaxis, ya sea que su array literal contiene un solo (@( 1 ) o múltiples elementos (@( 1, 2 )). Contraste esto con solo usar ,: 1, 2 vs. , 1.
  • No necesitas ,-separar las líneas de un multilínea@(...) declaraciones (aunque tenga en cuenta que cada línea se convierte técnicamente en su propia declaración).
  • No hay errores de precedencia de operadores, porque $(...) y @(...) tienen la mayor precedencia.

[2] PetSerAl proporciona este fragmento de código avanzado para mostrar el escenarios limitados en los que PowerShell lo hace respeto elencos, concretamente en el contexto de resolución de sobrecarga para llamadas a métodos .NET:

# Define a simple type that implements an interface
# and a method that has 2 overloads.
Add-Type '
  public interface I  string M(); 
  public class C : I 
           string I.M()        return "I.M()";  // interface implementation
    public string M(int i)     return "C.M(int)";  
    public string M(object o)  return "C.M(object)";  
  
'
# Instantiate the type and use casts to distinguish between
# the type and its interface, and to target a specific overload.
$C = New-Object C
$C.M(1)           # default: argument type selects overload -> 'C.M(int)' 
([I]$C).M()       # interface cast is respected -> 'I.M()'
$C.M([object]1)   # argument cast is respected -> 'C.M(object)'

Debido a que no ha especificado el tipo de datos del array explícitamente.

Por ejemplo, asignar un número entero a $x[1] funcionaría, porque el arrayel tipo es Object[].

Si especifica un tipo de datos al construir el array, no podrá asignar valores de un tipo incompatible más adelante:

C:PS> [int[]] $myArray = 12,64,8,64,12

C:PS> $myArray.GetType()

IsPublic IsSerial Name                                     BaseType                   
-------- -------- ----                                     --------                   
True     True     Int32[]                                  System.Array               



C:PS> $myArray[0] = "asd"
Cannot convert value "asd" to type "System.Int32". Error: "Input string was not in a c
orrect format."
At line:1 char:1
+ $myArray[0] = "asd"
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastFromStringToInteger

Reseñas y calificaciones del artículo

Si guardas alguna indecisión o disposición de regenerar nuestro enunciado puedes ejecutar una ilustración y con placer lo interpretaremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *