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

25
elmah.io logo 24
26

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 an sbyte[] 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 an is check like the above) it returns the original reference - so that's happening here.

  • ToList() delegates to the constructor of List<T> taking an IEnumerable<T>

  • That constructor is optimized for ICollection<T> to use CopyTo... and that's what's failing. Here's a version which has no method calls other than CopyTo:

    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...