System.ArrayTypeMismatchException
The exception that is thrown when an attempt is made to store an element of the wrong type within an array.
Minimum version: >= 1.1 >= Core 1.0
Statistics
How to handle it
try
{
}
catch (System.ArrayTypeMismatchException e)
{
}
try
{
}
catch (System.ArrayTypeMismatchException e) when (e.Message.Contains("something"))
{
}
try
{
}
catch (System.ArrayTypeMismatchException e) when (LogException(e))
{
}
private static bool LogException(Exception e)
{
logger.LogError(...);
return false;
}
How to avoid it
We haven't written anything about avoiding this exception yet. Got a good tip on how to avoid throwing System.ArrayTypeMismatchException? Feel free to reach out through the support widget in the lower right corner with your suggestions.
Links
YouTube videos
Possible fixes from StackOverflow
Okay, this really depends on a few oddities combined:
Even though in C# you can't cast a
byte[]
to ansbyte[]
directly, the CLR allows it:var foo = new byte[] {246, 127}; // This produces a warning at compile-time, and the C# compiler "optimizes" // to the constant "false" Console.WriteLine(foo is sbyte[]); object x = foo; // Using object fools the C# compiler into really consulting the CLR... which // allows the conversion, so this prints True Console.WriteLine(x is sbyte[]);
Cast<T>()
optimizes such that if it thinks it doesn't need to do anything (via anis
check like the above) it returns the original reference - so that's happening here.ToList()
delegates to the constructor ofList<T>
taking anIEnumerable<T>
That constructor is optimized for
ICollection<T>
to useCopyTo
... and that's what's failing. Here's a version which has no method calls other thanCopyTo
:object bytes = new byte[] { 246, 127 }; // This succeeds... ICollection<sbyte> list = (ICollection<sbyte>) bytes; sbyte[] array = new sbyte[2]; list.CopyTo(array, 0);
Now if you use a Select
at any point, you don't end up with an ICollection<T>
, so it goes through the legitimate (for the CLR) byte
/sbyte
conversion for each element, rather than trying to use the array implementation of CopyTo
.
UPDATED WITH THE SOLUTION
Okay, this was very tricky. I have no idea how you manage to get this type of project. You might have upgraded your project type from an earlier version of ASP.NET MVC. Any way below is the answer.
In you Web.Config's runtime Assembly binding section, please add the below.
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
This should fix your problem.
You can use .Select(e => (Test)e)
.
assembly.GetTypes()
returns a RuntimeType[]
, but it uses array type covariance to disguise it as a Type[]
. Per the docs for the Span
constructor, an ArrayTypeMismatchException
is thrown if T
(which in this case is Type
) does not match the runtime array type (which in this case is RuntimeType
).
assembly.GetTypes().ToArray()
on the other hand returns an actual Type[]
: it will create a brand new array based on the declared type T
, which is Type
, so you get a true Type[]
array and there is no mismatch -- T
is Type
and you're passing a true Type[]
to the Span
constructor.
ReadOnlySpan<T>
does not have this constraint, since it is not possible to write an incompatible Type
to a read-only RuntimeType[]
, so you can do this:
new ReadOnlySpan<Type>(assembly.GetTypes());
What is happening here? Let's start with foreach
loop, C# compiler optimizes them heavily especially when you use them with arrays - they are basically changes to normal for(int i = 0; i < length; i++) { }
- so the example with
foreach (var i in ((int[])units))
{
System.Console.WriteLine(i.GetType());
System.Console.WriteLine(i);
}
cannot be trusted, BTW foreach
can also perform cast on element type it is better to try generic Array.GetValue
method:
int[] x = ((int[])uints);
Console.WriteLine(x.GetValue(0).GetType()); // System.UInt32
Console.WriteLine(x[0].GetType()); // System.Int32
So even access x[0]
can return already casted value but Array.GetValue
returned what was already there an uint
.
Let's do another experiment:
Console.WriteLine(x.GetType()); // System.UInt32[]
Console.WriteLine(uints.GetType()); // System.UInt32[]
Console.WriteLine(Object.ReferenceEquals(x, uints)); // True
This assures us that cast in var x = (int[])uints
is a NOP - no operation, it does nothing at all. Especially 3th line show us the we get exactly the same instance.
Now inside List constructor we have lines
_items = new T[count];
c.CopyTo(_items, 0);
that actually throw Array mismatch exception.
But why this exception wasn't thrown earlier e.g. when GetEnumerator()
is called I don't know myself, I expected exception to be thrown on lines
x.GetEnumerator()
because types IEnumerable<int>
and IEnumerable<uint>
are not compatible but none was - maybe because .NET returns here System.Array+SZArrayEnumerator
that is the same for every value type array.
EDIT: (After example with Cat)
Array covariance in C# assures us that any array of reference types can be assigned to object[]
and that array of type Subclass[]
can be assigned to BaseClass[]
. The case with value types is different since they can have different sizes and/or conversion behaviours (uint
vs int
).
ToList
uses internally Array.Copy
call, when we look at Array.Copy
implementation in CRL: https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/classlibnative/bcltype/arraynative.cpp#L328 we see that array can be copied only if value types have compatible singess which is checked by another util function https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/invokeutil.h#L196
The other question is why it was implemented this way...
Source: Stack Overflow