[C#.NET] 利用 Expression Tree 提昇反射效率-動態屬性

[C#.NET] 利用 Expression Tree 提昇反射效率-動態屬性

續上篇,[C#.NET] 利用 Expression Tree 提昇反射效率 Expression 能夠寫出高效率的反射

這篇將處理類別屬性

 

使用 Reflection 類別處理屬性,核心片斷程式碼如下


{
    if (instance == null)
    {
        return;
    }
    var propertyInfo = instance.GetType().GetProperty(memberName);
    if (propertyInfo == null)
    {
        return;
    }
    propertyInfo.SetValue(instance, newValue, null);
}

private static object Get(object instance, string memberName)
{
    if (instance == null)
    {
        return null;
    }
    var propertyInfo = instance.GetType().GetProperty(memberName);
    if (propertyInfo == null)
    {
        return null;
    }

    return propertyInfo.GetValue(instance, null);
}

 

完整程式碼如下:

https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.Expressions/Simple.Expressions/ReflectionProperty.cs


使用 Expression 類別處理屬性


{
    var type = typeof(T);
    var instance = Expression.Parameter(typeof(object), "instance");
    var memberName = Expression.Parameter(typeof(string), "memberName");
    var nameHash = Expression.Variable(typeof(int), "nameHash");
    var getHashCode = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode")));
    var cases = new List<SwitchCase>();
    foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
    {
        var property = Expression.Property(Expression.Convert(instance, typeof(T)), propertyInfo.Name);
        var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int));

        cases.Add(Expression.SwitchCase(Expression.Convert(property, typeof(object)), propertyHash));
    }
    var switchEx = Expression.Switch(nameHash, Expression.Constant(null), cases.ToArray());
    var methodBody = Expression.Block(typeof(object), new[] { nameHash }, getHashCode, switchEx);
    return Expression.Lambda<Func<object, string, object>>(methodBody, instance, memberName).Compile();
}

private static Action<object, string, object> GenerateSetValue()
{
    var type = typeof(T);
    var instance = Expression.Parameter(typeof(object), "instance");
    var memberName = Expression.Parameter(typeof(string), "memberName");
    var newValue = Expression.Parameter(typeof(object), "newValue");
    var nameHash = Expression.Variable(typeof(int), "nameHash");
    var getHashCode = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode")));
    var cases = new List<SwitchCase>();
    foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
    {
        var property = Expression.Property(Expression.Convert(instance, typeof(T)), propertyInfo.Name);
        var setValue = Expression.Assign(property, Expression.Convert(newValue, propertyInfo.PropertyType));
        var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int));

        cases.Add(Expression.SwitchCase(Expression.Convert(setValue, typeof(object)), propertyHash));
    }
    var switchEx = Expression.Switch(nameHash, Expression.Constant(null), cases.ToArray());
    var methodBody = Expression.Block(typeof(object), new[] { nameHash }, getHashCode, switchEx);

    return Expression.Lambda<Action<object, string, object>>(methodBody, instance, memberName, newValue).Compile();
}

 

完整程式碼如下:

https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.Expressions/Simple.Expressions/DynamicProperty.cs


接著我將要測試它們運行的效果

    1. 測直接處理屬性
    2. Expression反射
    3. Reflection反射
  • 測試次數:776286
  • 片斷程式碼如下

public void GetAndSetValue_Test()
{
    var runTimes = 776286;
    var expected = new FactProductInventory();

    var dynamicProperty = new DynamicProperty<FactProductInventory>();
    var reflectionProperty = new ReflectionProperty<FactProductInventory>();

    var test1 = new TestInfo(() =>
    {
        var inventory = new FactProductInventory();
        inventory.DateKey = expected.DateKey;
        inventory.MovementDate = expected.MovementDate;
        inventory.ProductKey = expected.ProductKey;
        inventory.UnitCost = expected.UnitCost;
        inventory.UnitsBalance = expected.UnitsBalance;
        inventory.UnitsIn = expected.UnitsIn;
        inventory.UnitsOut = expected.UnitsOut;
    }, "直接指定,GetAndSetValue");
    test1.Run(runTimes);

    var test2 = new TestInfo(() =>
    {
        var inventory = new FactProductInventory();
        foreach (var propertyName in s_propertyNames)
        {
            var field = propertyName.Key;
            var value = dynamicProperty.GetValue(expected, field);
            dynamicProperty.SetValue(inventory, field, value);
        }
    }, "Expression反射,GetAndSetValue");
    test2.Run(runTimes);

    var test3 = new TestInfo(() =>
    {
        var inventory = new FactProductInventory();

        foreach (var propertyName in s_propertyNames)
        {
            var field = propertyName.Key;
            var value = reflectionProperty.GetValue(expected, field);
            reflectionProperty.SetValue(inventory, field, value);
        }
    }, "Reflection反射,GetAndSetValue");
    test3.Run(runTimes);

    Assert.AreEqual(test1.RunCount, runTimes);
    Assert.AreEqual(test2.RunCount, runTimes);
    Assert.AreEqual(test3.RunCount, runTimes);
}

 

完整程式碼:https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.Expressions/UnitTestProject1/UnitTest1.cs

執行結果如下圖:

image

image

image

 

 

 

結論:

直接處理屬性是最快的,若需要動態的處理屬性,這時使用 Expression Tree,可以得到較好的性能

參考來源:

http://www.cnblogs.com/nankezhishi/archive/2012/02/11/dynamicaccess.html

http://www.cnblogs.com/artech/archive/2011/03/27/ExpressTreeVsIlEmit.html

http://www.cnblogs.com/artech/archive/2011/03/24/PropertyAccessor.html

http://blog.163.com/dreamman_yx/blog/static/26526894201092825312949/

 


本文出自:http://www.dotblogs.com.tw/yc421206/archive/2015/03/16/150737.aspx

專案連結:https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.Expressions/

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo