Подтвердить что ты не робот

Эффективность отражения - создание делегата (свойства С#)

У меня проблемы с производительностью с использованием отражения.
Поэтому я решил создать делегатов для свойств моих объектов и до сих пор получил это:

TestClass cwp = new TestClass();
var propertyInt = typeof(TestClass).GetProperties().Single(obj => obj.Name == "AnyValue");
var access = BuildGetAccessor(propertyInt.GetGetMethod());
var result = access(cwp);
static Func<object, object> BuildGetAccessor(MethodInfo method)
{
    var obj = Expression.Parameter(typeof(object), "o");

    Expression<Func<object, object>> expr =
        Expression.Lambda<Func<object, object>>(
            Expression.Convert(
                Expression.Call(
                    Expression.Convert(obj, method.DeclaringType),
                    method),
                typeof(object)),
            obj);

    return expr.Compile();
}

Результаты были весьма удовлетворительными, примерно в 30-40 раз быстрее, чем при использовании обычного метода (PropertyInfo.GetValue (obj, null);)

Проблема: Как я могу создать свойство SetValue свойства, которое работает одинаково? К сожалению, не получилось.

Я делаю это, потому что я не могу использовать методы с <T> из-за структуры моего приложения.

4b9b3361

Ответ 1

Это должно сработать для вас:

static Action<object, object> BuildSetAccessor(MethodInfo method)
{
    var obj = Expression.Parameter(typeof(object), "o");
    var value = Expression.Parameter(typeof(object));

    Expression<Action<object, object>> expr =
        Expression.Lambda<Action<object, object>>(
            Expression.Call(
                Expression.Convert(obj, method.DeclaringType),
                method,
                Expression.Convert(value, method.GetParameters()[0].ParameterType)),
            obj,
            value);

    return expr.Compile();
}

Использование:

var accessor = BuildSetAccessor(typeof(TestClass).GetProperty("MyProperty").GetSetMethod());
var instance = new TestClass();
accessor(instance, "foo");
Console.WriteLine(instance.MyProperty);

С TestClass:

public class TestClass 
{
    public string MyProperty { get; set; }
}

Распечатывает:

Foo

Ответ 2

Я думаю, вам будет лучше с конструкцией CreateDelegate, если производительность - это ключ. Поскольку вы заранее знаете сигнатуру метода, который здесь всего GetGetMethod и GetSetMethod для PropertyInfo, вы можете создать делегат для непосредственного выполнения самого метода с одной и той же сигнатурой. Выражения будут лучше подойти, если вам нужно создать некоторую логику (для которой у вас не было дескриптора метода) для делегатов. Я сделал несколько тестов на разных путях к этой проблеме:

Func<S, T> Getter;
Action<S, T> Setter;
PropertyInfo Property;
public void Initialize(Expression<Func<S, T>> propertySelector)
{
    var body = propertySelector.Body as MemberExpression;
    if (body == null)
        throw new MissingMemberException("something went wrong");

    Property = body.Member as PropertyInfo;



    //approaches:

    //Getter = s => (T)Property.GetValue(s, null);

    //Getter = memberSelector.Compile();

    //ParameterExpression inst = Expression.Parameter(typeof(S));
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Property(inst, Property), inst).Compile();

    //var inst = Expression.Parameter(typeof(S));
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Call(inst, Property.GetGetMethod()), inst).Compile();

    //Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), Property.GetGetMethod());



    //Setter = (s, t) => Property.SetValue(s, t, null);

    //var val = Expression.Parameter(typeof(T));
    //var inst = Expression.Parameter(typeof(S));
    //Setter = Expression.Lambda<Action<S, T>>(Expression.Call(inst, Property.GetSetMethod(), val),
    //                                         inst, val).Compile();

    //Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), Property.GetSetMethod());
}


//Actual calls (tested under loop):
public T Get(S instance)
{
    //direct invocation:
    //return (T)Property.GetValue(instance, null);

   //calling the delegate:
   //return Getter(instance);
}
public void Set(S instance, T value)
{
    //direct invocation:
    //Property.SetValue(instance, value, null);

   //calling the delegate:
   //Setter(instance, value);
}

Результаты около 10000000 вызовов - (Get, Set):

GetValue-SetValue (прямой): 3800 мс, 5500 мс

GetValue-SetValue (делегат): 3600 мс, 5300 мс

скомпилированные выражения:

   Get: Expression.Property: 280 ms

        Expression.Call: 280 ms

        direct compile: 280 ms
   Set: 300 ms

создать делегат: 130 мс, 135 мс

вызов прямого свойства: 70 мс, 70 мс

Я бы в вашем случае написал:

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;
}

Итак, теперь вы вызываете:

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

Разве это не проще? Здесь был написан общий класс для обработки точной вещи.

Ответ 3

Используйте динамические типы. Они используют отражение под капотом, но они намного быстрее.

В противном случае...

Существует множество бесплатных библиотек с быстрым отражением там с разрешительными лицензиями. Я бы связал вас, но их слишком много, и я не уверен, что вам подойдет. Просто найдите codeplex и т.д. Когда вы найдете что-то, что вам нравится, попробуйте.

Но да, может быть, до этого, подумайте, действительно ли отражение является ответом. Часто есть и другие решения.

Изменить: как запрошено...

http://geekswithblogs.net/SunnyCoder/archive/2009/06/26/c-4.0-dynamics-vs.-reflection.aspx
http://theburningmonk.com/2010/09/performance-test-dynamic-method-invocation-in-csharp-4/
http://www.mssoftwareconsulting.com/msswc/blog/post/C-40-and-dynamic-performance.aspx

Общеизвестно, насколько я могу судить.