[Feature] Would it be possible to declare fields?
See original GitHub issueHey, just discovered this package, looks amazing! Thank you for your time building this! 😄
I have a question/proposal, would it be possible to support declaring fields? This would be useful when the type of such fields is not accessible from C# (eg. with internal
types from other assemblies). Accessing and using those fields from IL would be easy, but from the readme it doesn’t look like it’s currently possible to declare the first right now? 🤔
Rationale
In the Microsoft.Toolkit.HighPerformance
package (source here, API browser here) we have a number of types that basically act equivalent for the System.ByReference<T>
type from CoreCLR, which is unfortunately internal
(made a proposal about making it public
a while back (here), but that’s not really planned). The workaround I came up with is to just use a Span<T>
as proxy for the ByReference<T>
type, using MemoryMarshal.CreateSpan<T>(ref T, int)
. This is kinda ok when we can reuse the Span<T>.Length
property for other purposes (basically as if it was just another int
field in the parent type), but it’s just unnecessary overhead when that’s not needed. Specifically, in the Ref<T>
and ReadOnlyRef<T>
types.
I was toying with the idea of just writing a part of that type directly in IL, declaring a ByReference<T>
field and simply using that directly from the various available APIs. Of course, this would only be for .NET Core 2.1, .NET Core 3.1 and .NET 5, and not for the other targets. Would something like this be possible? I’m actually curious in general even outside of this specific case 😄
Something like (just the idea):
[LocalField("System.ByReference`1", "reference")]
public readonly ref struct Ref<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ref(ref T value)
{
Type
byRefType = Type.GetType("System.ByReference`1").MakeGenericType(typeof(T)),
parameterType = typeof(T).MakeByRefType();
MethodRef cctor = MethodRef.Constructor(byRefType, parameterType);
Ldarg_0();
Ldarg_1();
Newobj(cctor);
Stfld(FieldRef.Field(byRefType, "reference"));
}
public ref T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Type byRefType = Type.GetType("System.ByReference`1").MakeGenericType(typeof(T));
Ldarg_0();
Ldflda(FieldRef.Field(byRefType, "reference"));
Call(MethodRef.PropertyGet(byRefType, "Value"));
return ref IL.ReturnRef<T>();
}
}
}
Again congrats again for building this, I’ve been looking for something like this for a while! 🚀
Issue Analytics
- State:
- Created 3 years ago
- Comments:23 (11 by maintainers)
Top GitHub Comments
Yeah getting Fody involved would make the whole thing even harder to maintain, you’re right. After all, this is basically just a temporary workaround, ideally I’d just want to switch to proper
ref T
fields if/when they will become available (maybe C# 10 and .NET 6? Who knows!). This is an incredibly powerful workaround for the moment though! 😄 And it’s already miles better than just hacking around with a fakeSpan<T>
with unused length.About that
.deps.json
file, not sure, but I guess someone reviewing that PR will be able to chime in on that. Yeah I’d agree with you, this solution is effectively much easier to implement than actually writing new support for declaring fields with InlineIL. That said, I’ll still definitely be using your lib in the future in other projects, as it’s just great! 🚀You’re welcome! 😄
This could also have been implemented in a slightly different way, for example by using a Fody in-solution weaver that would inject the necessary code and assembly reference, but it would probably be harder to read and maintain than the way I made it.
One more thing I thought of after posting this:
System.Private.CoreLib
will be referenced in the.deps.json
file. I don’t know if it’s an issue, hopefully not.I guess I won’t be adding the feature you asked for InlineIL in the end, since the way I’ve shown here solves the problem much better anyways.