I've had to update a vb.net project from .NetFramework 4 to .NetFramework 4.7.2. In the process the following code is now throwing an error
Dim actuatorModelsArr = DirectCast(retNumberList, Array) Dim
Dim actuatorModels = actuatorModelsArr.Cast(Of ACTUATORMODELS)().ToList()
The error is System.ArrayTypeMismatchException: Source array type cannot be assigned to destination array type.
retNumberList is an Integer array ACTUATORMODELS is an Enum
in the .netFramework 4 version actuatorModels is a list of the Enum {System.Collections.Generic.List`1[FisherIECLib.ACTUATORMODELS]}
The list is used later in the module via linq to grab one of the Enums as a return value.
Is there a way around this or a way to create a list of the Enum?
Thanks in advance, Hank
CodePudding user response:
I think it's interesting that it stopped working. You can replace the code with a Select and cast to make it work
Dim retNumberList = {1, 2, 3, 4}
' either of these will produce a List of your Enum
Dim a = retNumberList.Select(Function(i) DirectCast(i, ACTUATORMODELS)).ToList()
Dim b = retNumberList.Select(Function(i) CType(i, ACTUATORMODELS)).ToList()
As for the original not working:
Dim c = retNumberList.Cast(Of ACTUATORMODELS)().ToList()
The literature suggests this is the equivalent of a (type)obj
c# style cast, but both versions of vb.net cast work in the select. I am not sure why.
CodePudding user response:
djv's answer helps fix your problem. Hopefully this answer will explain what's going wrong. If you look at the reference source for Cast(Of T)
on an untyped IEnumerable
, you'll find that the first thing that happens is the C# equivalent of this:
Dim asTyped = TryCast(source, IEnumerable(Of TResult))
If asTyped IsNot Nothing Then Return asTyped
Surprisingly, this cast will work for Integer()
to IEnumerable(Of ACTUATORMODELS)
. This sets the issue in motion, because when it comes to making a List(Of T)
out of the resulting sequence, it turns out that Integer()
and ACTUATORMODELS()
are actually not interchangeable, but Cast
has already treated the sequence as though they are.
Based on some testing, this issue seems to arise out of the interaction of this corner case with a corner case in how the List(Of T)
range constructor works and the more general corner case of Integer()
vs TEnum()
.
In the general case of iterating over Integer()
as if it were IEnumerable(Of TEnum)
, it works. You can make a For Each
loop over the sequence, the enumeration variable will have TEnum
as the type, and you'll see the values as if they were TEnum
.
The problem comes in the range constructor for List(Of T)
, and an optimization there for a source that implements ICollection(Of T)
. In that case, the ctor will try to use ICollection(Of T).CopyTo
to copy the items into the list's internal storage. This is where the error ultimately occurs, because (going back to the implementation of Cast(Of T)
) the source is still the Integer()
array, and Array.Copy
(via Array.CopyTo
) is not OK with trying to do that with a destination of TEnum()
.
I feel like this is a bug somewhere, though I'm not sure if it's in Cast(Of T
), the List(Of T)
range ctor, or in the array copy handling. I'm also not sure it's something that will get fixed, since it's a corner case that hits what look like significant optimizations and would require a very specialized check to catch.