21

Is this the fastest way to update a property using reflection? Assume the property is always an int:

PropertyInfo counterPropertyInfo = GetProperty();
int value = (int)counterPropertyInfo.GetValue(this, null);
counterPropertyInfo.SetValue(this, value + 1, null);
Luke Belbina
  • 5,708
  • 12
  • 52
  • 75

3 Answers3

28

I did some benchmarking here when you know the type arguments (a non generic approach wont be very different). CreateDelegate would be the fastest approach for a property if you can't directly access it. With CreateDelegate you get a direct handle to GetGetMethod and GetSetMethod of the PropertyInfo, hence reflection is not used every time.

public static Func<S, T> BuildGetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
    return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate<Func<S, T>>();
}

public static Action<S, T> BuildSetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
    return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate<Action<S, T>>();
}

// a generic extension for CreateDelegate
public static T CreateDelegate<T>(this MethodInfo method) where T : class
{
    return Delegate.CreateDelegate(typeof(T), method) as T;
}

public static PropertyInfo GetPropertyInfo<S, T>(this Expression<Func<S, T>> propertySelector)
{
    var body = propertySelector.Body as MemberExpression;
    if (body == null)
        throw new MissingMemberException("something went wrong");

    return body.Member as PropertyInfo;
}

So now you call:

TestClass cwp = new TestClass();
var access = BuildGetAccessor((TestClass t) => t.AnyValue);
var result = access(cwp);

Or even better you can encapsulate the logic in a dedicated class to have a get and set methods on it.

Something like:

public class Accessor<S>
{
    public static Accessor<S, T> Create<T>(Expression<Func<S, T>> memberSelector)
    {
        return new GetterSetter<T>(memberSelector);
    }

    public Accessor<S, T> Get<T>(Expression<Func<S, T>> memberSelector)
    {
        return Create(memberSelector);
    }

    public Accessor()
    {

    }

    class GetterSetter<T> : Accessor<S, T>
    {
        public GetterSetter(Expression<Func<S, T>> memberSelector) : base(memberSelector)
        {

        }
    }
}

public class Accessor<S, T> : Accessor<S>
{
    Func<S, T> Getter;
    Action<S, T> Setter;

    public bool IsReadable { get; private set; }
    public bool IsWritable { get; private set; }
    public T this[S instance]
    {
        get
        {
            if (!IsReadable)
                throw new ArgumentException("Property get method not found.");

            return Getter(instance);
        }
        set
        {
            if (!IsWritable)
                throw new ArgumentException("Property set method not found.");

            Setter(instance, value);
        }
    }

    protected Accessor(Expression<Func<S, T>> memberSelector) //access not given to outside world
    {
        var prop = memberSelector.GetPropertyInfo();
        IsReadable = prop.CanRead;
        IsWritable = prop.CanWrite;
        AssignDelegate(IsReadable, ref Getter, prop.GetGetMethod());
        AssignDelegate(IsWritable, ref Setter, prop.GetSetMethod());
    }

    void AssignDelegate<K>(bool assignable, ref K assignee, MethodInfo assignor) where K : class
    {
        if (assignable)
            assignee = assignor.CreateDelegate<K>();
    }
}

Short and simple. You can carry around an instance of this class for every "class-property" pair you wish to get/set.

Usage:

Person p = new Person { Age = 23 };
var ageAccessor = Accessor<Person>(x => x.Age);
int age = ageAccessor[p]; //gets 23
ageAccessor[p] = 45; //sets 45

Bit bad use of indexers here, you may replace it with dedicated "Get" and "Set" methods, but very intuitive to me :)

To avoid having to specify type each time like,

var ageAccessor = Accessor<Person>(x => x.Age);
var nameAccessor = Accessor<Person>(x => x.Name);
var placeAccessor = Accessor<Person>(x => x.Place);

I made the base Accessor<> class instantiable, which means you can do

var personAccessor = new Accessor<Person>();
var ageAccessor = personAccessor.Get(x => x.Age);
var nameAccessor = personAccessor.Get(x => x.Name);
var placeAccessor = personAccessor.Get(x => x.Place);

Having a base Accessor<> class means you can treat them as one type, for eg,

var personAccessor = new Accessor<Person>();
var personAccessorArray = new Accessor<Person>[] 
                          {
                           personAccessor.Get(x => x.Age), 
                           personAccessor.Get(x => x.Name), 
                           personAccessor.Get(x => x.Place);
                          };
Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • This is fantastic! It is very close to what I'm looking for, but I'm still fuzzy enough on all of this that I can't see the way to the solution. I've written a generic ToString method that can be passed any object and it will go and return a string consisting of the values of all of the public properties of the object. It uses PropertyInfo and GetValue to get each property. It is *slow*. I want to use your methods here, but it appears that I must know the name of the type at compile time. I don't. How can I adapt it? – Mark Meuer Apr 25 '13 at 19:38
  • 2
    Yes in my case, the object has to be *typed*, not a generic *object*. But do you really not have the *type* with you? Are they all merely *object* ? If you really have to cope with *object*, then you will have to rely on expression trees (I used CreateDelegate) with intermediate casts and like, but it will be still very fast. Something like shown here http://geekswithblogs.net/Madman/archive/2008/06/27/faster-reflection-using-expression-trees.aspx – nawfal Apr 25 '13 at 19:45
  • I'm not sure if I can understand your **exact** requirement in the scope of comments or even answer you here, but if it warrants, you can make it a question. Don't forget me to notify here in case ;) – nawfal Apr 25 '13 at 19:52
  • I think your link in the first comment is exactly what I'm looking for. I want a fast getter on an object whose type I do not know at compiile time. Thank you! – Mark Meuer Apr 26 '13 at 18:42
  • If I only have a string with full class name, and property name like MyNameSpace1.X.Y.Z.ClassName, and PropertyName ? – Kiquenet Jan 16 '14 at 14:26
  • @Kiquenet you can do it by normal reflection ([see here](http://stackoverflow.com/questions/9404523/set-property-value-using-property-name)), but I guess performance is important to you. In that case see the geekswithblog link in comments above which has getters and setters declared like `Func` and `Action`. – nawfal Jan 16 '14 at 14:55
  • if he could access those properties (private or not known in compile time) why would he want to looking for a solution using reflection? sorry but your Expression based approach renders useless in this case. – TakeMeAsAGuest Feb 28 '19 at 14:20
  • @TakeMeAsAGuest you are not entirely wrong, but this does find use in very limited cases. For e.g. you can carry around `Accessor` array without causing boxing penalties and later invoke it. It wouldn't be possible to have a generic `Func` collection of accessors for a class unless you use a `Func`. Now it needn't use expression trees or reflection for it, right, but I was also showing a way to bypass the `GetValue`, `SetValue` APIs using expression compiling. As I said its trivial to extend my example to act fully like dynamic accessors. – nawfal Feb 28 '19 at 20:08
  • @TakeMeAsAGuest In that sense you are right, dynamic case finds the most use here. I was basically caught up between two things I was trying to show. Editing this answer has been long overdue :) – nawfal Feb 28 '19 at 20:09
16

You should look at FastMember (nuget, source code], it's really fast comparing to reflection.

I've tested these 3 implementations:

The benchmark needs a benchmark function:

static long Benchmark(Action action, int iterationCount, bool print = true)
{
    GC.Collect();
    var sw = new Stopwatch();
    action(); // Execute once before

    sw.Start();
    for (var i = 0; i <= iterationCount; i++)
    {
        action();
    }

    sw.Stop();
    if (print) System.Console.WriteLine("Elapsed: {0}ms", sw.ElapsedMilliseconds);
    return sw.ElapsedMilliseconds;
}

A fake class:

public class ClassA
{
    public string PropertyA { get; set; }
}

Some test methods:

private static void Set(string propertyName, string value)
{
    var obj = new ClassA();
    obj.PropertyA = value;
}

private static void FastMember(string propertyName, string value)
{
    var obj = new ClassA();
    var type = obj.GetType();
    var accessors = TypeAccessor.Create(type);
    accessors[obj, "PropertyA"] = "PropertyValue";
}

private static void SetValue(string propertyName, string value)
{
    var obj = new ClassA();
    var propertyInfo = obj.GetType().GetProperty(propertyName);
    propertyInfo.SetValue(obj, value);
}

private static void SetMethodInvoke(string propertyName, string value)
{
    var obj = new ClassA();
    var propertyInfo = obj.GetType().GetProperty(propertyName);
    propertyInfo.SetMethod.Invoke(obj, new object[] { value });
}

The script itself:

var iterationCount = 100000;
var propertyName = "PropertyA";
var value = "PropertyValue";

Benchmark(() => Set(propertyName, value), iterationCount);
Benchmark(() => FastMember(propertyName, value), iterationCount);
Benchmark(() => SetValue(propertyName, value), iterationCount);
Benchmark(() => SetMethodInvoke(propertyName, value), iterationCount);

Results for 100 000 iterations:

Default setter : 3ms

FastMember: 36ms

PropertyInfo.SetValue: 109ms

PropertyInfo.SetMethod: 91ms

Now you can choose yours !!!

nawfal
  • 70,104
  • 56
  • 326
  • 368
Thomas
  • 24,234
  • 6
  • 81
  • 125
  • It can not use in private property. – lindexi Sep 26 '18 at 02:45
  • 2
    Thanks for the update but private properties are not supposed to be set from outside the class scope ??? – Thomas Sep 26 '18 at 08:36
  • 1
    I was in the process of writing `FastMember` myself :-) when I found this answer. I've used BenchmakrDotNet to measure it against many alternatives like AutoMapper, CasltleDictionaryAdapter, caching the propertyInfo, caching an action to set the property and, of course, hand-coded property assignment. It was only 36% slower than direct property assignments, and three-times faster than the next fastest alternative. So I gave up writing my own and will be using Marc's FastMember thanks to you! Nice. – Loudenvier Jun 06 '22 at 21:48
11

Just be sure that you are caching the PropertyInfo somehow, so that you aren't repeatably calling type.GetProperty. Other than that it would probably be faster if you created a delegate to a method on the type that performed the increment, or like Teoman suggested make the type implement an interface and use that.

BrandonAGr
  • 5,827
  • 5
  • 47
  • 72