Por algum motivo, o powershell não me deixa converter a string[]
em an object[]
, mas permite para todos os outros tipos. O exemplo abaixo mostra que funciona para vários tipos, exceto string.
[int[]]$intary = 2,4
[char[]]$chrary = 'a','b'
[bool[]]$booary = $true,$false
[DateTime[]]$datary = (Get-Date),(Get-Date)
[string[]]$strary = 'c','d'
$splary = 'e,f' -split ',' # -split returns [string[]] type specifically
[object[]]$objaryi = @()
[object[]]$objaryc = @()
[object[]]$objaryb = @()
[object[]]$objaryd = @()
[object[]]$objarys = @()
[object[]]$objarysp = @()
$intary.GetType() # Int32[]
$chrary.GetType() # Char[]
$booary.GetType() # Boolean[]
$datary.GetType() # DateTime[]
$strary.GetType() # String[]
$splary.GetType() # String[]
$objaryi.GetType() # Object[]
'conversions'
$objaryi = $intary # Int32[] converted to Object[]
$objaryi.GetType() # Object[]
$objaryc = $chrary # Char[] converted to Object[]
$objaryc.GetType() # Object[]
$objaryb = $booary # Boolean[] converted to Object[]
$objaryb.GetType() # Object[]
$objaryd = $datary # DateTime[] converted to Object[]
$objaryd.GetType() # Object[]
$objarys = $strary # NOT converted to Object[]; instead changes type of $objarys to String[]
$objarys.GetType() # String[]
$objarysp = $splary # NOT converted to Object[]; instead changes type of $objarysp to String[]
$objarysp.GetType() # String[]
$objarys = @($strary) # the array subexpression operator @() DOES convert to Object[]
$objarys.GetType() # Object[]
$objarysp = @($splary) # the array subexpression operator @() DOES convert to Object[]
$objarysp.GetType() # Object[]
'inline cast'
$objarys = @([string[]]@(1,2)) # the array subexpression operator @() does not work on an inline cast [1]
$objarys.GetType() # String[]
Uma razão pela qual isso importa é que se você tentar armazenar um hash em um array que é tipado, string[]
o powershell converterá silenciosamente sua definição de hash para uma string. A string é o sting para o caminho completo typename do hash.
$objaryi[0] = @{a=1;b=2} # can hold a hashtable because it's now an Object[]
$objaryi[0].GetType() # Hashtable
$objaryi[0]['b'] # 2
$objarys[0] = @{a=1;b=2} # silently converts to a string because it's a String[]
$objarys[0].GetType() # String
$objarys[0]['b'] # $null; perhaps unexpected
$objarys[0] # the String value "System.Collections.Hashtable"
$objarys[0].Length # 28
Com relação ao uso de
@(...)
, o operador de subexpressão de matriz , para criar uma[object[]]
cópia do tipo _ de uma matriz:Seu código funciona como está no PowerShell (Core) 7 ; ou seja,
@(...)
produz de forma confiável uma[object[]]
matriz do tipo , independentemente do tipo da matriz de entrada e independentemente de você usar uma conversão literal (por exemplo,[string[]]
dentro de@(...)
)No Windows PowerShell (a edição legada, fornecida com o Windows, somente para Windows do PowerShell, cuja versão mais recente e atual é a 5.1), você precisa de uma solução alternativa ao usar uma conversão literal dentro de
@(...)
, porque a v5.1 tem uma otimização mal aconselhada que otimiza o@(...)
away neste caso (uma otimização que foi posteriormente revertida no PowerShell 7 ):@([string[]] (42, 43))
como exemplo:Conforme sugerido acima, simplesmente omitir a
[string[]]
conversão resultará em uma[object[]]
matriz.Se você precisar do elenco, use a seguinte solução alternativa:
@([string[]] (42, 43) | Write-Output -NoEnumerate)
[object[]]
matriz.Sempre que
@(...)
funciona como pretendido, observe que, embora seja uma maneira conveniente de criar uma cópia[object[]]
do tipo _ de uma determinada matriz, também é ineficiente :Envolve enumerar o array, ou seja, processar seus elementos um por um e, então, coletá-los em um array (invariavelmente novo ), sempre do tipo
[object[]]
).Veja a próxima seção para alternativas de melhor desempenho.
Com relação ao uso de restrições/casts do tipo array :
Observação:
Uma restrição de tipo em uma variável (por exemplo,
[object[]] $arrayO = ...
difere de uma conversão (por exemplo,$arrayO = [object[]] ...
) principalmente porque a primeira, em essência, reaplica implicitamente a conversão a futuras atribuições à mesma variável. [1]Estou me referindo e usando apenas conversões na discussão abaixo, mas o mesmo se aplica às restrições de tipo.
Daniel acertou em cheio em um comentário:
Surpreendentemente, o PowerShell - em ambas as edições - varia seu comportamento de conversão com base em se o tipo de matriz de entrada é (com base em) um tipo de valor .NET vs. um tipo de referência .NET . Os tipos de valor são os vários tipos numéricos
[int]
, por exemplo , , bem como tipos como[bool]
e[char]
que (invariavelmente diretamente ) derivam daSystem.ValueType
classe base abstrata: [2]A conversão de uma matriz baseada em tipo de valor .NET para qualquer outro tipo - incluindo
[object[]]
- resulta em uma matriz do tipo especificado (que é necessariamente uma cópia (de tipo modificado) da matriz de entrada).Converter um array baseado em tipo de referência
[object[]]
.NET para .NET é efetivamente uma operação sem efeito .De forma mais geral , a lógica é: se o tipo de conversão for um tipo base (direto ou indireto) do array de entrada, a conversão é efetivamente uma operação nula , o que significa que o array de entrada é passado por .
Como
[object]
(System.Object
) é a raiz da hierarquia de tipos .NET, segue-se que uma[object[]]
conversão não é uma operação com qualquer matriz baseada em tipo de referência .NET; para ilustrar que o mesmo se aplica às classes base em geral:Entretanto, observe que isso implica que o tratamento especial de matrizes baseadas em tipos de valor .NET é, em última análise, arbitrário , dado que os tipos de valor também derivam, em última análise, de
[object]
.Soluções alternativas para garantir que uma matriz de um tipo especificado seja construída:
Se o desempenho for primordial , use a seguinte
[Array]
abordagem baseada em:A abordagem acima é de longe a de melhor desempenho, mas é prolixa e não óbvia.
A próxima opção mais rápida - e muito mais lenta - é usar uma
foreach
instrução combinada com uma restrição de tipo:A abordagem conceitualmente mais direta - embora ainda mais lenta que a
foreach
abordagem anterior - é usar uma sobrecarga específica do método intrínseco.ForEach()
, que permite que você passe um literal de tipo para converter cada objeto de entrada; por exemplo:Note que o acima não produz um
[object[]]
array , mas uma instância do tipo[System.Collections.ObjectModel.Collection`1[object]]
, que, no entanto, em termos práticos, se comporta como um array .Se você precisa de um
[object[]]
array, especificamente, você pode simplesmente incluir o acima em@(...)
, o que, no entanto, envolve enumerar a coleção e então coletar os objetos enumerados em um array, o que aumenta a ineficiência.A opção mais lenta de longe é a solução alternativa mencionada na seção superior:
[1] Há uma exceção curiosa:
$b = [bool] 'foo'
e[bool] $b = 'foo'
não se comportam da mesma forma em sua atribuição (inicial); esta última causa um erro , porque a lógica de vinculação de parâmetros é aplicada inesperadamente: veja o problema do GitHub #10426 .[2] Para um determinado tipo, você pode facilmente determinar se é um tipo de valor .NET ou um tipo de referência .NET acessando a
.IsValueType
propriedade do tipo; por exemplo ,[int].IsValueType
is$true
, enquanto[string].IsValueType
is$false
No entanto, observe que as matrizes são sempre tipos de referência .NET ; por exemplo,
[int[]].IsValueType
is$false
.O C# fornece a
struct
palavra-chave para criar tipos de valor; o PowerShell não oferece uma maneira geral de criá-los (aclass
palavra-chave cria tipos de referência , embora umaenum
definição, especificamente, seja um tipo de valor).