我有一些用Set-Variable myVar -Value 10 -Option ReadOnly
语法设置的只读变量,但我也需要只读数组。如何制作只读数组?
谢谢
最佳答案
PetSerAl曾经无数次以简短的评论方式提供了解决方案。他还帮助改善了这个答案:
使用 [Array]::AsReadOnly($someArray)
(可在中使用)PSv3 + [1]
:
# Create a 2-element read-only collection containing strings.
$readOnlyColl = [Array]::AsReadOnly(('one', 'two'))
# Try to modify an element, which now fails:
$readOnlyColl[0] = 'uno'
在 PSv2 中,使用强制转换为
[System.Collections.ObjectModel.ReadOnlyCollection[<type>]]
,如下所示。上面产生了以下内容,表明不能修改元素:
Unable to index into an object of type System.Collections.ObjectModel.ReadOnlyCollection`1[System.String].
请注意,错误消息在某种程度上具有误导性,因为它只是不允许通过索引进行写访问,而读访问(例如
$readOnlyColl[0]
)则可以正常工作。尽管
$readOnlyColl
并非严格意义上的数组,但其行为类似于一个用于所有实际目的。
从技术上讲,
$readOnlyColl
是使用从输入数组的元素推断出的元素类型的通用类型System.Collections.ObjectModel.ReadOnlyCollection`1
的实例。[2]警告:返回集合只是数组的包装,并且,根据您应用的特定输入数组和强制类型转换,对输入数组元素的以后修改可能会反射(reflect)在包装器集合中。[3]
如果要显式控制元素的数据类型,请使用强制转换。
例如,根据
[string]
值创建[int]
类型的集合:# Cast to an array of the desired type first.
[Array]::AsReadOnly([string[]] (1, 2, 3)
# Alternatively, cast to the target collection type directly.
# Always use this in PSv2, where [Array]::AsReadOnly() cannot be called.
[System.Collections.ObjectModel.ReadOnlyCollection[string]] (1, 2, 3)
与常规PowerShell数组一样,对
[object[]]
类型的元素使用[object]
/ [object]
,但是请注意,如果输入数组确实已经是[object[]]
数组,则结果集合将是该数组的包装器-请参见脚注[2]。只是为了说明,为什么
Set-Variable myVar -Option ReadOnly
不足以创建只读数组:它使变量为只读,这意味着您不能为其分配其他值。相比之下,不会阻止修改恰好存储在变量中的数据的属性(例如数组的元素)。[1]该方法自建立PSv2的.NET v2开始可用;但是,只有PSv3引入了直接调用通用方法的功能,因此需要PSv3 +。但是,在PSv2中,您可以直接将其转换为
[System.Collections.ObjectModel.ReadOnlyCollection[<type>]]
。[2]也就是说,即使PowerShell默认情况下会创建
[object[]]
数组,但如果实际元素碰巧都是同一类型,那么PowerShell会选择该特定类型,而不是[object]
;在当前情况下,由于输入数组的两个元素都是字符串,因此在幕后创建了一个新的[string[]
类型的数组,生成的只读集合将其包装为[System.Collections.ObjectModel.ReadOnlyCollection[string]]
类型。[3]因为该集合只是输入数组的包装,所以有权访问输入数组的任何人仍可以修改其元素,并且包装集合将反射(reflect)该更改。
但是,在PowerShell中,如果PowerShell恰巧在幕后创建一个新数组,然后将其传递给
[Array]::AsReadOnly()
,则通常可以避免该潜在问题。假设您没有在[object[]]
调用中将此类数组显式转换为[object[]]
,则会为PS数组([Array]::AsReadOnly()
)创建一个新数组,其元素都恰好具有相同的类型。对于特定类型的输入数组(例如[int[]]
),仅当您强制转换为其他类型(例如[string[]]
)时才创建新数组;如果未创建新数组,则说明包装器问题:$arr = [int[]] (1..3); $coll = [Array]::AsReadOnly($arr); $arr[1] = 42; $coll[1]
-$coll[1]
现在反射(reflect)42
,即通过基础数组$a
分配的更改后的值。