C
C#2mo ago
Tinister

Reflection with InlineArrayAttribute?

If I have an object o that I know is a struct with InlineArrayAttribute, how would I go about getting/setting each of the items? I can get the length with o.GetType().GetCustomAttribute<InlineArrayAttribute>()?.Length And I can get its field with o.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Single() But using fieldInfo.GetValue/fieldInfo.SetValue seems to only get/set the item at index 0 in the array.
12 Replies
boiled goose
boiled goose2mo ago
(are you passing index in SetValue?) (also if you know the type cannot you cast it?)
Tinister
Tinister2mo ago
If I could cast it I wouldn't be using reflection in the first place.
boiled goose
boiled goose2mo ago
maybe there could be known or easier cases
Tinister
Tinister2mo ago
It's a utility method that walks the object graph using reflection When I add inline arrays it doesn't walk each item in it
Foxtrek_64
Foxtrek_642mo ago
What is preventing you from casting, out of curiosity? I think the easiest option here would be to make sure that every type you're attempting to handle can be described by a common interface. Then you should be able to cast it to that interface and get the info you need from that. Is there anything preventing you from using an interface here?
Tinister
Tinister2mo ago
I use the same utility method to walk List<T> and other sorts of BCL types. I can't add an interface there.
boiled goose
boiled goose2mo ago
this could work
var a = (Array)fieldInfo.GetValue(instance);
a.SetValue(value, index);
var a = (Array)fieldInfo.GetValue(instance);
a.SetValue(value, index);
at least it's work a try
Tinister
Tinister2mo ago
No that's a cast exception
Foxtrek_64
Foxtrek_642mo ago
Not sure if this helps, but here you can see if o is a kind of List<T> when you don't know what T is if (o.GetType() == typeof(List<>).MakeGeneric(o.GetType().GetGenericArguments()[0])) { ... } This will throw an out of bounds exception if the type isn't generic, so do keep that in mind
boiled goose
boiled goose2mo ago
mmm well this is wrong this shouldn't get the field, since the declaring type should already be an array yeah i don't exactly know what type an InlineArray is under the hood, but it's not the usual array maybe you can ask in #allow-unsafe-blocks
reflectronic
reflectronic2mo ago
you basically can't accessing the elements of an InlineArray is done through unsafe code that is very hard to do with reflection with a pinned GCHandle you can probably just barely get away with something, but that means it won't work for any InlineArrays of reference types
Tinister
Tinister2mo ago
I remain undeterred After looking at how the compiler synthesizes converting inline arrays to span, this is what I'm thinking:
if (type.GetCustomAttribute<InlineArrayAttribute>() is { } inlineAttr)
{
Delegate helper = inlineHelper<int, int>;
MethodInfo helperMethod = helper.Method.GetGenericMethodDefinition().MakeGenericMethod(type, fields.Single().FieldType);
return helperMethod.Invoke(helper.Target, [o, inlineAttr.Length]);
}

TArray inlineHelper<TArray, TField>(TArray array, int length)
{
Span<TField> span = MemoryMarshal.CreateSpan(ref Unsafe.As<TArray, TField>(ref array), length);
for (int i; i < length; i++)
span[i] = (TField)visit(span[i]);
return array;
}
if (type.GetCustomAttribute<InlineArrayAttribute>() is { } inlineAttr)
{
Delegate helper = inlineHelper<int, int>;
MethodInfo helperMethod = helper.Method.GetGenericMethodDefinition().MakeGenericMethod(type, fields.Single().FieldType);
return helperMethod.Invoke(helper.Target, [o, inlineAttr.Length]);
}

TArray inlineHelper<TArray, TField>(TArray array, int length)
{
Span<TField> span = MemoryMarshal.CreateSpan(ref Unsafe.As<TArray, TField>(ref array), length);
for (int i; i < length; i++)
span[i] = (TField)visit(span[i]);
return array;
}