Powershell: Arraylist converts to Array but after destruction cannot be re-created

Created on 6 May 2020  路  10Comments  路  Source: PowerShell/PowerShell

Hi there,

in powershell v7 (I think) I found an issue with Arraylists. When I create an arraylist and then destroy it after conversation, it throws an exception. I think this is a bug, isnt it?

Thanks,

Alex

$myAl = New-Object System.Collections.ArrayList
   $myAl.Add('15')
   [array]$myAl = $myAl
   $myAl = $null
   $myAl = New-Object System.Collections.ArrayList
   $myAl.Add('15')

Exception:

MethodInvocationException:
Line |
6 | $myAl.Add('15')
| ~~~
| Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."

Issue-Question

Most helpful comment

We're getting much more into .NET territory here than what PowerShell has any control over, but to explain somewhat...

System.Array (which is the base class for System.Object[] as well as every other type that is a basic array) implements the IList interfaces which, among other things, demands that it provides an Add() method. So it does, to comply with the interface. However, the interface only states it has to support the method, not that the method needs to _work_.

There's another IList member that it implements, you'll note that there's an IsFixedSize member property in your Get-Member result there; the expectation is that if IsFixedSize is true, the Add() and similar methods are expected to throw / error out instead of doing something to the list.

All 10 comments

Hmm... nah, this is expected behaviour. 馃檪

So, when you declare a variable with the type cast on the left like this:

[array]$myVar = 1

That type cast is recorded as an attribute against the variable itself, and will persist until a new type cast is declared, or you remove the variable explicitly with Remove-Variable.

Any new value you set to that variable will automatically be cast to that type (or an error will be reported if there is no cast possible).

Your ArrayList is only arraylist until it hits that variable which you're defining to be a simple array. Once it's an array, it's a fixed-size collection and cannot be modified.

@vexx32 your explanation is right but $myAl has add method and it does not work

,$myAl | gm


   TypeName: System.Object[]

Name           MemberType            Definition
----           ----------            ----------
Count          AliasProperty         Count = Length
Add            Method                int IList.Add(System.Object value)
Address        Method                System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a...
Clear          Method                void IList.Clear()
Clone          Method                System.Object Clone(), System.Object ICloneable.Clone()
CompareTo      Method                int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo...
Contains       Method                bool IList.Contains(System.Object value)
CopyTo         Method                void CopyTo(array array, int index), void CopyTo(array array, long index), void...
Equals         Method                bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ...
GetEnumerator  Method                System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ...
GetHashCode    Method                int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu...
GetLength      Method                int GetLength(int dimension)
GetLongLength  Method                long GetLongLength(int dimension)
GetLowerBound  Method                int GetLowerBound(int dimension)
GetType        Method                type GetType()
GetUpperBound  Method                int GetUpperBound(int dimension)
GetValue       Method                System.Object GetValue(Params int[] indices), System.Object GetValue(int index)...
IndexOf        Method                int IList.IndexOf(System.Object value)
Initialize     Method                void Initialize()
Insert         Method                void IList.Insert(int index, System.Object value)
Remove         Method                void IList.Remove(System.Object value)
RemoveAt       Method                void IList.RemoveAt(int index)
Set            Method                void Set(int , System.Object )
SetValue       Method                void SetValue(System.Object value, int index), void SetValue(System.Object valu...
ToString       Method                string ToString()
Item           ParameterizedProperty System.Object IList.Item(int index) {get;set;}
IsFixedSize    Property              bool IsFixedSize {get;}
IsReadOnly     Property              bool IsReadOnly {get;}
IsSynchronized Property              bool IsSynchronized {get;}
Length         Property              int Length {get;}
LongLength     Property              long LongLength {get;}
Rank           Property              int Rank {get;}
SyncRoot       Property              System.Object SyncRoot {get;}

We're getting much more into .NET territory here than what PowerShell has any control over, but to explain somewhat...

System.Array (which is the base class for System.Object[] as well as every other type that is a basic array) implements the IList interfaces which, among other things, demands that it provides an Add() method. So it does, to comply with the interface. However, the interface only states it has to support the method, not that the method needs to _work_.

There's another IList member that it implements, you'll note that there's an IsFixedSize member property in your Get-Member result there; the expectation is that if IsFixedSize is true, the Add() and similar methods are expected to throw / error out instead of doing something to the list.

Adding onto what @vexx32 said, IList.Add is an explicit interface implementation. PowerShell doesn't surface this distinction very well, but in C# that method can't be used unless you are explicitly casting the array as IList

object[] myArray = new object[0];

// Compiles
((IList)myArray).Add(0);

// Doesn't compile
myArray.Add(0);

but some other methods like IList.Clear, IList.IndexOf work and it gets confused.

@scriptingstudio Yeah Clear works because it's of a fixed size, not read only (it doesn't change the size of the array, it just nulls out every item). Why wouldn't IndexOf work?

I would suggest that inactive methods should be hidden.

It's not inactive, it throws by design. There's no way for PowerShell to determine that.

I get the frustration, I really do. When I first ran into this I was incredibly confused by it. Sometimes design patterns that make a lot of sense in C# are going to be surfaced in PowerShell in a really unavoidably bad way, there's not much that can really be done about it. I wish when they were initially designing the binder they required a cast to access explicitly implemented members, but they already didn't do that. Unfortunately if they changed it to work like that now it would be a pretty large breaking change.

Thank you guys for all your patient support and explanations.

this code Works

```$myAl = New-Object System.Collections.ArrayList
$myAl.Add('15')
[array]$myAl = $myAl
Remove-Variable myAl
$myAl = New-Object System.Collections.ArrayList
$myAl.Add('15')

Was this page helpful?
0 / 5 - 0 ratings