-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C# needs some way to transition between generic type restrictions without boxing or reflection #7909
Comments
Yep. this is a common problem. It also showed up:
Probably elsewhere that I don't remember. |
dotnet/runtime#89439 (comment) could possibly solve this too. |
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsBackground: I'm working on a library for unmanaged memory arenas in C#. These arenas take two generic types:
I want methods to allocate both types to the arena. Not all types are under control by the user after all (stuff like primitive types like
Option number 1 is a bad option. Methods can't be overloaded by generic type restrictions, so you'd wind up with two differently named methods for the two different types. Not only that, you could pass structs which implement That leaves option 2, which while annoying does actually work. You have one method, and do reflection shenanigans to call the interface, even without boxing if you put your back into it. This is the clearly superior option. Problem: NativeAOT is unhappy about option 2. Especially once you add in the code for unboxed interface method calls there's really no guarantee that you can safely ignore the reflection warnings it rightfully throws at you. Unfortunately there is, to my knowledge, no way to transition from a generic method call In the end I managed to solve this problem specifically for the This solution is also not ideal. It works for The ideal solution for this would be non-NativeAOT specific and would involve C# in general obtaining some kind of "trust me bro" syntax that lets you forcibly call if (myStruct is ISomeInterface)
((ISomeInterface)myStruct).MyMethod(); Of course none of this has ever been a big issue because reflection could be used to (jankily) solve the problem, but now with NativeAOT taking a more prominent role in the .NET ecosystem this patchwork solution is no longer sufficient for the problem.
|
Agreed that this is a fundamental C# restriction. I'm moving to csharplang. |
Duplicate of #6308. |
Background: I'm working on a library for unmanaged memory arenas in C#. These arenas take two generic types:
T : unmanaged
T : unmanaged, IArenaContents
I want methods to allocate both types to the arena. Not all types are under control by the user after all (stuff like primitive types like
int
for example) so I can't simply restrict everything toIArenaContents
. Under regular .NET this leaves me with two choices:unmanaged
, and dynamically detectIArenaContents
instances and use reflection to dispatch to the relevant interface methods. Doing this without boxing takes a bit of work, but is possible.Option number 1 is a bad option. Methods can't be overloaded by generic type restrictions, so you'd wind up with two differently named methods for the two different types. Not only that, you could pass structs which implement
IArenaContents
into the method that isn't meant for it. You can't limit generic types by exclusion, so the best you can do is check this at runtime and then throw an exception. This means creating a more brittle implementation because errors won't be caught at compile-time, and a small mistake is enough to take down an entire application.That leaves option 2, which while annoying does actually work. You have one method, and do reflection shenanigans to call the interface, even without boxing if you put your back into it. This is the clearly superior option.
Problem: NativeAOT is unhappy about option 2. Especially once you add in the code for unboxed interface method calls there's really no guarantee that you can safely ignore the reflection warnings it rightfully throws at you. Unfortunately there is, to my knowledge, no way to transition from a generic method call
T : unmanaged
to a generic, unboxed, method callT : unmanaged, IWhatever
. I've been code golfing for days without any success at making this work.In the end I managed to solve this problem specifically for the
unmanaged
case by havingIArenaContents
implementors return an object with methods that take anIntPtr
, cast it to their typeT*
, and then invoke the method. I can cache this method object for each type, and indirectly call the methods (sans boxing) throughIntPtr
.This solution is also not ideal. It works for
unmanaged
, but it will not work forstruct
as there is no generic unboxed type to cast them to and back likeIntPtr
. In this case, there would simply be no fix for this issue, and it would result in worse, less robust code and more crashing bugs from runtime type checks.The ideal solution for this would be non-NativeAOT specific and would involve C# in general obtaining some kind of "trust me bro" syntax that lets you forcibly call
struct, ISomeInterface
generic functions (without boxing, obviously.) I've been informed that the following pattern can do that, but that it's reliant on the JIT, brittle, and I couldn't actually find any confirmation that it even works, works in NativeAOT (which does not have a JIT), or let alone works reliably enough to be an actual solution in any circumstance.Of course none of this has ever been a big issue because reflection could be used to (jankily) solve the problem, but now with NativeAOT taking a more prominent role in the .NET ecosystem this patchwork solution is no longer sufficient for the problem.
The text was updated successfully, but these errors were encountered: