由于某种原因,powershell 不允许我将 a 转换string[]
为 an,object[]
但允许将所有其他类型转换为 an。下面的示例显示它适用于除字符串之外的几种类型。
[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[]
这很重要的一个原因是,如果您尝试将哈希存储在类型为string[]
powershell 的数组中,则会默默地将您的哈希定义转换为字符串。该字符串是哈希的完整路径类型名称的字符串。
$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
关于使用
@(...)
数组子表达式运算符来创建[object[]]
数组的类型副本:您的代码在PowerShell (Core) 7中按原样工作;也就是说,可靠地生成一个-typed 数组,而不管输入数组的类型如何,也不管您是否使用文字转换(例如在 内部)
@(...)
[object[]]
[string[]]
@(...)
在Windows PowerShell(旧版、随 Windows 一起提供、仅适用于 Windows 的 PowerShell 版本,其最新版本为 5.1)中,当您在 中使用文字转换时需要一种解决方法,因为 v5.1 有一个不明智的优化,在这种情况下优化了 (该优化后来在PowerShell 7中被恢复):
@(...)
@(...)
@([string[]] (42, 43))
举例来说 :正如上面所暗示的,简单地省略转换
[string[]]
就会导致一个[object[]]
数组。如果确实需要转换,请使用以下解决方法:
@([string[]] (42, 43) | Write-Output -NoEnumerate)
[object[]]
。只要
@(...)
按预期工作,请注意,虽然这是创建给定数组的类型副本的便捷方法,但效率也很低:[object[]]
它涉及枚举数组,即逐个处理其元素,然后将它们收集到(始终是新的)数组中,该数组始终为类型
[object[]]
。请参阅下一部分以了解性能更佳的替代方案。
关于使用数组类型约束/强制类型转换:
笔记:
变量的类型约束(例如,)与强制
[object[]] $arrayO = ...
类型转换(例如,$arrayO = [object[]] ...
)的主要区别在于,前者本质上隐式地将强制类型转换重新应用于对同一变量的未来赋值。 [1]我在下面的讨论中仅仅引用和使用强制类型转换,但同样适用于类型约束。
Daniel 的评论一针见血:
令人惊讶的是,PowerShell(两个版本)根据输入数组类型是(基于).NET值类型还是 .NET引用类型来改变其转换行为。值类型是各种数字类型,例如
[int]
,以及诸如[bool]
和等类型[char]
,它们(总是直接)从抽象System.ValueType
基类派生:[2]将基于.NET值类型的数组转换为任何其他类型(包括
[object[]]
)都会产生指定类型的数组(这必然是输入数组的(类型修改的)副本)。将 .NET基于引用类型的数组转换为
[object[]]
实际上是无操作的。更一般地,逻辑是:如果转换类型是输入数组的(直接或间接)基类型,则转换实际上是无操作的,这意味着输入数组被传递。
由于
[object]
(System.Object
) 是.NET 类型层次结构的根,因此对于任何基于 .NET 引用类型的数组,[object[]]
强制转换都是无操作的;为了说明同样的情况也适用于一般的基类:但是,请注意,这意味着对 .NET 基于值类型的数组的特殊处理最终是任意的,因为值类型最终也是从中派生出来的
[object]
。确保构造指定类型的数组的解决方法:
如果性能至关重要,请
[Array]
改用以下方法:上述方法是迄今为止效果最好的方法,但既冗长又不明显。
下一个最快的选项——速度要慢得多——是使用
foreach
与类型约束相结合的语句:概念上最直接的方法 - 尽管比该
foreach
方法更慢 - 是使用内在.ForEach()
方法的特定重载,它允许您传递类型文字来强制转换每个输入对象;例如:请注意,上面的代码不会输出一个
[object[]]
数组,而是一个类型的实例[System.Collections.ObjectModel.Collection`1[object]]
,但实际上,它的行为类似于数组。具体来说,如果你确实需要一个
[object[]]
数组,你可以简单地将上面的代码括在里面@(...)
,但这需要枚举集合,然后将枚举的对象收集到一个数组中,这会增加效率。迄今为止最慢的选项是上面提到的解决方法:
[1] 有一个奇怪的异常:
$b = [bool] 'foo'
和在其(初始)赋值中[bool] $b = 'foo'
表现不同;后者会导致错误,因为参数绑定逻辑被意外应用:参见GitHub 问题 #10426。[2] 对于给定类型,您可以通过访问类型的属性轻松确定它是 .NET值类型还是 .NET引用类型;例如is ,而is 但是,请注意,数组本身始终是 .NET引用类型;例如is 。 C# 提供了用于创建值类型的关键字; PowerShell没有提供创建它们的通用方法(关键字创建引用类型,但定义具体来说是一种值类型)。
.IsValueType
[int].IsValueType
$true
[string].IsValueType
$false
[int[]].IsValueType
$false
struct
class
enum