Skip to content

MetadataLoadContext: Type.GetGenericArguments() returns RoType[] instead of Type[] #124214

@smdn

Description

@smdn

Description

In MetadataLoadContext, calling GetGenericArguments() on a Type object returns an array instance of type RoType[] (an internal implementation type) rather than a standard Type[].

This causes issues in code that expects the returned array to be a strict Type[].

For example, as shown in the reproduction code below, calling AsSpan() on the result of GetGenericArguments() throws an ArrayTypeMismatchException.
This occurs because AsSpan<Type>() cannot be called on an array that is physically RoType[], even though RoType inherits from Type, due to array covariance limitations with spans.

Reproduction Steps

using System.Reflection;
using System.Runtime.InteropServices;

using var mlc = new MetadataLoadContext(
  new PathAssemblyResolver([
    Assembly.GetExecutingAssembly().Location,
    .. Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll")
  ])
);

// Use MetadataLoadContext.LoadFromAssemblyPath to load the executing assembly and
// get the Type of the following class C<T1, T2>.
var a = mlc.LoadFromAssemblyPath(Assembly.GetExecutingAssembly().Location);
// var a = Assembly.GetExecutingAssembly();
var t = a.GetType(typeof(C<,>).FullName!, throwOnError: true)!;

Console.WriteLine(t.GetType());
Console.WriteLine($"Return type of GetGenericArguments: {t.GetGenericArguments().GetType()}");
Console.WriteLine($"GetGenericArguments: {string.Join(", ", t.GetGenericArguments().AsSpan())}");

class C<T1, T2> { }

As shown in the "Actual behavior" section, this code throws an ArrayTypeMismatchException when calling AsSpan().

Expected behavior

Type.GetGenericArguments() should return a Type[], and calling AsSpan() should succeed without throwing an exception.

Return type of GetGenericArguments: System.Type[]
GetGenericArguments: T1, T2

Actual behavior

Type.GetGenericArguments() returns RoType[], which leads to an ArrayTypeMismatchException when AsSpan() is invoked.

Return type of GetGenericArguments: System.Reflection.TypeLoading.RoType[]
Unhandled exception. System.ArrayTypeMismatchException: Attempted to access an element as a type incompatible with the array.
   at Program.<Main>$(String[] args) in /home/smdn/temp/GetGenericArgumentsAsSpan/Program.cs:line 18

Regression?

No response

Known Workarounds

If a strict Type[] is required, the array must be re-created using Cast<Type>().ToArray() or similar methods:

t.GetGenericArguments().Cast<Type>().ToArray().AsSpan();

Configuration

$ dotnet --info
.NET SDK:
 Version:           10.0.101
 Commit:            fad253f51b
 Workload version:  10.0.100-manifests.1773493e
 MSBuild version:   18.0.6+fad253f51

Runtime Environment:
 OS Name:     ubuntu
 OS Version:  24.04
 OS Platform: Linux
 RID:         ubuntu.24.04-x64
 Base Path:   /usr/lib/dotnet/sdk/10.0.101/

csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Reflection.MetadataLoadContext" Version="10.0.2"/>
  </ItemGroup>
</Project>

Other information

In the current implementation of RoType.GetGenericArguments(), the method returns a copy of the array using CloneArray():

public override Type[] GetGenericArguments() => GetGenericArgumentsNoCopy().CloneArray();

Specifying Type as the explicit generic argument for CloneArray should ensure the resulting array is created as Type[] instead of preserving the original RoType[] type.
This change may resolve the issue (I haven't actually verified if it works):

         //  Don't seal since we may need to convert any modified types to unmodified.
-        public override Type[] GetGenericArguments() => GetGenericArgumentsNoCopy().CloneArray();
+        public override Type[] GetGenericArguments() => GetGenericArgumentsNoCopy().CloneArray<Type>();

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions