1

I need to create a ReadOnlyCollection from elements of an array but it seems like ReadOnlyCollection elements can only be defined in declaration of the collection. Is there any other way than listing each element of the array in the declaration of the collection as in the following sample?

[byte[]]$arr=10,20,30
[System.Collections.ObjectModel.ReadOnlyCollection[byte]]$readOnly=
 $arr[0],$arr[1],$arr[2]

Thanks

2 Answers2

1

Pass the array to the constructor instead:

$readOnly = New-Object 'System.Collections.ObjectModel.ReadOnlyCollection[byte]' -ArgumentList @(,$arr)

or (PowerShell 5.0 and up):

$readOnly = [System.Collections.ObjectModel.ReadOnlyCollection[byte]]::new($arr)

Now, your question title specifically says copy array elements - beware while you won't be able to modify $readOnly, its contents will still reflect changes to the array that it's wrapping:

PS C:\> $arr[0] = 100
PS C:\> $arr[0]
100
PS C:\> $readOnly[0]
100

If you need a completely separate read-only collection, copy the array to another array first and then overwrite the variable reference with the read-only collection:

$readOnly = [byte[]]::new($arr.Count)
$arr.CopyTo($readOnly, 0)
$readOnly = [System.Collections.ObjectModel.ReadOnlyCollection[byte]]::new($readOnly)

Now you can modify $arr without affecting $readOnly:

PS C:\> $arr[0] = 100
PS C:\> $arr[0]
100
PS C:\> $readOnly[0]
10
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • There's no need to copy since you can turn the array itself into the read-only collection while retaining all of the elements. Thanks –  Aug 19 '18 at 18:57
  • 1
    @bp2017 depends on context, if you're taking an array as a non-pipelined parameter argument in a function you can't know that that's the only reference to it for example :) – Mathias R. Jessen Aug 19 '18 at 19:47
0

Mathias R. Jessen's helpful answer contains good alternatives and background information, but in your case you can simply assign your array to your type-constrained $readOnly variable:

[byte[]] $arr=10,20,30 
[System.Collections.ObjectModel.ReadOnlyCollection[byte]] $readOnly = $arr

In PSv3+ you can alternatively use [Array]::AsReadOnly():

[byte[]] $arr=10,20,30 
$readOnly = [Array]::AsReadOnly($arr) # shorter, but doesn't lock in the type of $readOnly

As stated, System.Collections.ObjectModel.ReadOnlyCollection<T> only wraps its input array, so later changes to the input array's elements would be reflected in the read-only collection.

However, whether it wraps your actual input array, depends on its specific type, because Powershell may create a new array for you behind the scenes - see this answer for details.

If in doubt, use $arr.Clone() to explicitly create a copy of your array.

mklement0
  • 382,024
  • 64
  • 607
  • 775