C# Reflection: Fastest Way to Update a Property Value?
Asked Answered
M

3

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);
Maryalice answered 28/5, 2011 at 0:5 Comment(2)
I see that you pass this as the instance. Why don't you just set the property directly?Molal
The fastest way would be such classes implement an IIncrementable interface and use it and not reflectionIsisiskenderun
G
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.

Graybill answered 28/5, 2011 at 0:39 Comment(0)
C
27

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);
                          };
Carmelocarmen answered 18/4, 2013 at 12:25 Comment(9)
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?Sawicki
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 geekswithblogs.net/Madman/archive/2008/06/27/…Carmelocarmen
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 ;)Carmelocarmen
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!Sawicki
If I only have a string with full class name, and property name like MyNameSpace1.X.Y.Z.ClassName, and PropertyName ?Anodic
@Anodic you can do it by normal reflection (see here), 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<object, object> and Action<object, object>.Carmelocarmen
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.Spithead
@Spithead you are not entirely wrong, but this does find use in very limited cases. For e.g. you can carry around Accessor<T> array without causing boxing penalties and later invoke it. It wouldn't be possible to have a generic Func<S, ?> collection of accessors for a class unless you use a Func<S, object>. 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.Carmelocarmen
@Spithead 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 :)Carmelocarmen
T
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 !!!

Tisza answered 10/12, 2015 at 3:11 Comment(3)
It can not use in private property.Maurits
Thanks for the update but private properties are not supposed to be set from outside the class scope ???Tisza
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.Camire
G
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.

Graybill answered 28/5, 2011 at 0:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.