C# FieldInfo reflection alternatives
Asked Answered
C

1

1

I am currently using FieldInfo.GetValue and FieldInfo.SetValue quite a lot in my programm, which is significantly slowing up my programm.

For PropertyInfo I use the GetValueGetter and GetValueSetter methods so I only use reflection once for a given type. For the FieldInfo, the methods don't exist.

What is the suggested approach for FieldInfo?

EDIT: I followed this useful link from CodeCaster's reply. This is a great search direction.

Now the "only" point that I don't get in this answer is how I can cache the getters / setters and re-use them in a generic way, using only the field's name - which is basically what the SetValue is doing

// generate the cache
Dictionary<string, object> setters = new Dictionary<string, object>();
Type t = this.GetType();
foreach (FieldInfo fld in t.GetFields()) {
     MethodInfo method = t.GetMethod("CreateSetter");
     MethodInfo genericMethod = method.MakeGenericMethod( new Type[] {this.GetType(), fld.FieldType});
     setters.Add(fld.Name, genericMethod.Invoke(null, new[] {fld}));
}
// now how would I use these setters?
setters[fld.Name].Invoke(this, new object[] {newValue}); // => doesn't work without cast....
Coralyn answered 22/7, 2016 at 14:11 Comment(6)
Not using reflection unless you absolutely need it.Raybin
How is the FieldInfo obtained? If you know the name of the field during compile-time, I suggest making a pair of lambda expression for set and get the value.Whirlybird
@Raybin Not a duplicate, at least not of the linked question. Please note that the question in link is about PropertyInfo, this one about FieldInfo. There's a significant difference in these two.Whirlybird
@Illidan that doesn't matter, the discussion is still the same. Cache the Property|FieldInfo objects, use Reflection.Emit to actually emit the IL that assigns the fields instead of going the reflection way (or use a library that does so) and so on, all of which the OP can use to do further research. See also Is there a way to create a delegate to get and set values for a FieldInfo?. Enough has been written on this subject already, and one can only select one duplicate. This question is in no way unique, nor well-researched.Raybin
@Raybin That's a much better link to help the questioner, thanks.Whirlybird
instead of us trying to guess what will make things faster you should explain to us why you need to use reflection?Brittain
L
1

You could use Expressions to generate faster field accessors. If you want them to work on Object rather than the field's concrete type, you'd have to add casts (Convert) in the expression.

using System.Linq.Expressions;

class FieldAccessor
{
    private static readonly ParameterExpression fieldParameter = Expression.Parameter(typeof(object));
    private static readonly ParameterExpression ownerParameter = Expression.Parameter(typeof(object));

    public FieldAccessor(Type type, string fieldName)
    {
        var field = type.GetField(fieldName,
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        if (field == null) throw new ArgumentException();

        var fieldExpression = Expression.Field(
            Expression.Convert(ownerParameter, type), field);

        Get = Expression.Lambda<Func<object, object>>(
            Expression.Convert(fieldExpression, typeof(object)),
            ownerParameter).Compile();

        Set = Expression.Lambda<Action<object, object>>(
            Expression.Assign(fieldExpression,
                Expression.Convert(fieldParameter, field.FieldType)), 
            ownerParameter, fieldParameter).Compile();
    }

    public Func<object, object> Get { get; }

    public Action<object, object> Set { get; }
}

Usage:

class M
{
    private string s;
}

var accessors = new Dictionary<string, FieldAccessor>();

// expensive - you should do this only once
accessors.Add("s", new FieldAccessor(typeof(M), "s"));

var m = new M();
accessors["s"].Set(m, "Foo");
var value = accessors["s"].Get(m);
Legitimate answered 23/7, 2016 at 5:7 Comment(2)
I tried to compile your example. It appears the types are missing from the static readonly fields. I tried to use "Expression", but then the compiler could not resolve the call to Expression.Lambda() with those parameters. OK. So "ParameterExpression" is what is needed.Newlywed
Also had to add private set; for "Get" and "Set". Or is that a new C# 6.0 syntax? Implied private set?Newlywed

© 2022 - 2024 — McMap. All rights reserved.