Effective loop reflection

I'm having performance issues when using reflection in loops. The fact is that I use it for multiple access to objects at the end of a long network of dependencies. For example, in such a situation

class FirstObject 
{
    public SecondObject sO;
}

class SecondObject
{
    public ThirdObject tO;
}

class ThirdObject
{
    public FourthObject fO;
}

class FourthObject
{
    public object neededValue;
}

Since I'm only interested in the value contained in the last object, I need to repeatedly navigate the entire chain with GetProperty().GetValue()

FirstObject → SecondObject → ThirdObject → FourthObject [requiredValue]

Is there any way, maybe some kind of API that can be used to shorten the chains or just keep it all the way to neededValuein such situations?

Explanation

I need to do this with a list containing FirstObjects. I can’t rewrite the code to reduce the level of nesting: it is generated automatically.

+4
2

, GetValue(). , .

object GetPropertyValue(object obj, string propertyName)
{
    MethodInfo propertyGetter = obj.GetType().GetMethod("get_" + propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
    Func<object> getPropertyValue = (Func<object>)Delegate.CreateDelegate(typeof(Func<object>), obj, propertyGetter);
    return getPropertyValue();
}

"", , getter - , : get_<PropertyName>().

propertyGetter , .


UPDATE

getter . , ( ):

Func<ObjectType, object> getPropertyValue = (Func<ObjectType, object>)Delegate.CreateDelegate(typeof(Func<ObjectType, object>), propertyGetter);

ObjectType obj;
var propertyValue = getPropertyValue(obj);

getPropertyValue(), , GetValue().

+2

, , Func<object, object> . GetProperty/GetField GetValue.

- - System.Linq.Expressions.Expression:

public static class SelectorFactory
{    
    public static Func<object, object> GetSelector(Type type, string memberPath)
    {
        return CreateSelector(type, memberPath);
    }

    static Func<object, object> CreateSelector(Type type, string memberPath)
    {
        var parameter = Expression.Parameter(typeof(object), "source");
        var source = Expression.Convert(parameter, type);
        var value = memberPath.Split('.').Aggregate(
            (Expression)source, Expression.PropertyOrField);
        if (value.Type.IsValueType)
            value = Expression.Convert(value, typeof(object));
        // (object source) => (object)((T)source).Prop1.Prop2...PropN
        var selector = Expression.Lambda<Func<object, object>>(value, parameter);
        return selector.Compile();
    }
}

:

// This would be outside of the loop
var selector = SelectorFactory.GetSelector(typeof(FirstObject), "sO.tO.fO.neededValue");
// and this inside (of course instead of new you would get item from a list)
var item = new FirstObject { sO = new SecondObject { tO = new ThirdObject { fO = new FourthObject { neededValue = "Ivan" } } } };
var value = selector(item);

P.S. , ( ), , - , , :

static readonly Dictionary<Tuple<Type, string>, Func<object, object>> selectorCache = new Dictionary<Tuple<Type, string>, Func<object, object>>();

public static Func<object, object> GetSelector(Type type, string memberPath)
{
    var key = Tuple.Create(type, memberPath);
    Func<object, object> value;
    lock (selectorCache)
    {
        if (!selectorCache.TryGetValue(key, out value))
            selectorCache.Add(key, value = CreateSelector(type, memberPath));
    }
    return value;
}
+1

Source: https://habr.com/ru/post/1660907/


All Articles