Skip to content
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

Unsafe.OffsetOf to get field offsets in types #43285

Closed
PJB3005 opened this issue Oct 11, 2020 · 11 comments
Closed

Unsafe.OffsetOf to get field offsets in types #43285

PJB3005 opened this issue Oct 11, 2020 · 11 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Reflection

Comments

@PJB3005
Copy link
Contributor

PJB3005 commented Oct 11, 2020

Background and Motivation

We wanted to quickly set attribute-tagged fields in user defined types at runtime. The fastest and most flexible way to do this would be various Unsafe APIs like AddByteOffset and As to get refs to the field and to then set them (obviously caching the field offsets ahead of time). The problem is that it's kind of extremely annoying to get the actual offset of individual fields: the best way I could figure out is using DynamicMethod to generate a tiny method that returns a ref to the field or the offset directly (because there is no FieldInfo.GetValueRef or something), then to Unsafe.ByteOffset<T> it.

So I simply propose an API that gets the in-memory offset of a field from the object's memory layout.

Proposed API

namespace System.Runtime.CompilerServices
{
    public class Unsafe
    {
        public int OffsetOf<T>(string fieldName);
        public int OffsetOf(Type t, string fieldName);
        public int OffsetOf(FieldInfo field);
    }
}

Usage Example

private static void SetField<T>(object o, int offset, T value)
{
	// or some better way to get a base ref to the class, see below in "nuisances".
    var asDummy = Unsafe.As<FieldOffsetDummy>(o);
    ref var @ref = ref Unsafe.Add(ref asDummy.A, offset);
    ref var oRef = ref Unsafe.As<byte, T>(ref @ref);
    oRef = value;
}

private sealed class FieldOffsetDummy
{
    public byte A;
}

var offset = Unsafe.OffsetOf(typeof(Foo), "Bar");
var foo = new Foo();

SetField(foo, offset, "hello");

Some nuisances

There isn't really a "clean" way to get a base reference this field offset can be used with. You can define a new class with a single byte field and then get a ref to that (after Unsafe.As) but that's far from clear or obvious.

So maybe an API to actually fetch the base reference for an object would go along with this aswell.

@PJB3005 PJB3005 added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Oct 11, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Runtime.CompilerServices untriaged New issue has not been triaged by the area owner labels Oct 11, 2020
@PJB3005 PJB3005 changed the title Unsafe.OffsetOf Unsafe.OffsetOf to get field offsets in types Oct 11, 2020
@BreyerW
Copy link

BreyerW commented Oct 11, 2020

Basically dupe of #28001.

Btw while we are on this topic i would also like overload based on RuntimeFieldHandle. This is already possible but requires dependency on internal implementation details. Useful when u have FieldInfo but not object itself

Edit:

This is what im talking about: https://stackoverflow.com/questions/30817924/obtain-non-explicit-field-offset

Interestingly enough it still works on net 5

Edit:

You already included such overload but i think my link is still useful for those who cant wait or as reference for possible impl

Just a trivia this method work for private fields too which is useful for source generators

@obiwanjacobi
Copy link

Or int FieldInfo.ByteOffset {get;}?

@PJB3005
Copy link
Contributor Author

PJB3005 commented Oct 12, 2020

Other methods like e.g. Unsafe.SizeOf<T> could "also" be Type.Size or something but aren't. That's the logic for this one being on Unsafe aswell.

@obiwanjacobi
Copy link

Unsafe is all about unsafe pointer manipulation (by looks of it). SizeOf<T>() was there to provide a similar function as Marshal.SizeOf does: report the true size in bytes of a type, which the normal (C#) sizeof() does not do in all circumstances correctly (AFAIK).
I feel adding these proposed methods would bring Unsafe out of focus, dilute it.
There is nothing inherently unsafe about querying for a byte-offset, is there?

@PJB3005
Copy link
Contributor Author

PJB3005 commented Oct 12, 2020

Yes but these byte offsets are only ever useful when used with unsafe operations like the example provided in the issue.

@GrabYourPitchforks
Copy link
Member

IMO #23716 is a better way to handle this. We really shouldn't be encouraging devs to perform arbitrary interior pointer manipulation on CLR objects which have been unsafe cast to an invalid / dummy type. There's no stated guarantee that the CLR supports such manipulation.

@obiwanjacobi
Copy link

Would the new Roslyn code generator extensions not cover much of these requirements?
More you can do at compile time the better, right?

@BreyerW
Copy link

BreyerW commented Oct 14, 2020

@obiwanjacobi as far as i know you cant access private data with source generators currently unless u can link IL code somehow with source generator (important for databinding+serializers) since we dont have blessed reflection-free way to do it (offsetof on FieldInfo or similar alternative allows this scenario but theres no blessed\existing api for that) and source generators dont allow rewrites so its not like roslyn generator is panacea for all related problems

@Joe4evr
Copy link
Contributor

Joe4evr commented Oct 17, 2020

@BreyerW Source generators generate a partial for the class it triggers on, so it can access all the private data you want.

@BreyerW
Copy link

BreyerW commented Oct 17, 2020

@Joe4evr right forgot about that however to be fully useable we must get relaxed partial first which is championed for c# 10 since it was discovered as pain point for source generators (currently it requires that all partials are decorated with this keyword preventing from extending types that we cant modify)

Edit scratch that i was remembering wrong it seems we still need some sort of offsetof functionality since source generators are insufficient due to partials limitations

@steveharter
Copy link
Member

Closing; assuming #45152 supersedes this.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Reflection
Projects
None yet
Development

No branches or pull requests

8 participants