runtime: Marshal.SizeOf(Type) is throwing incorrectly on generic types
Description
Marshal.SizeOf(typeof(somegenericstruct)) throws ArgumentException when T is a closed generic type. It should only throw on open generic types.
Here’s the implementation
public static int SizeOf(Type t)
{
if (t is null)
{
throw new ArgumentNullException(nameof(t));
}
if (!t.IsRuntimeImplemented())
{
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(t));
}
if (t.IsGenericType) // This should be changed to IsGenericTypeDefinition, I think
{
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t));
}
return SizeOfHelper(t, throwIfNotMarshalable: true);
}
Configuration
Seen on .NET 5.0
Analysis
The documentation says that ArgumentException is thrown if t is a generic type definition. The implementation however throws when it is any generic type (including a closed one). The implementation of the similar method
public static int SizeOf<T>(T structure)
does not throw this exception - it can be used as workaround.
Here’s some test code:
public class FrameworkBehaviorTests
{
[Fact]
public void CannotGetSizeOfOpenGenericType()
{
// This exception is expected
Assert.Throws<ArgumentException>(() =>
{
Marshal.SizeOf(typeof(GenericStruct<>));
});
}
[Fact]
public void CanGetSizeOfOpenGenericType()
{
// This test fails and throws as well
Assert.Equal(8, Marshal.SizeOf(typeof(GenericStruct<short>)));
}
[Fact]
public void CanGetSizeOfOpenGenericTypeViaInstance()
{
// This test passes, because it uses the SizeOf<T>(T object) overload
GenericStruct<short> gs;
gs._data1 = 2;
gs._data2 = 10;
Assert.Equal(8, Marshal.SizeOf(gs));
}
private struct GenericStruct<T>
{
public T _data1;
public int _data2;
}
}
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 1
- Comments: 17 (9 by maintainers)
When generic interop types were made possible, it was really just relaxing the restriction in the interop logic so it could cross the managed/native boundary.
There wasn’t also work done to update
Marshal.*to support this and much of that was done based on the feedback @AaronRobinsonMSFT has already given above.Put another way, these types must already be blittable and so you don’t want to use any of the
MarshalAPIs.Unsafe.SizeOf,Unsafe.WriteUnaligned,NativeMemory.Alloc, and other APIs are all faster and better for blittable data. The same goes for directly taking/passingMyStruct<T>*and using pointer types directly.This is all “inline” with an effort to move users off of the built-in marshalling support and onto things like
DllImportGeneratorwhich generates and works off blittable data behind the scenes.