C#中運算式樹狀架構和反射來訪問對象屬性的效能比較淺析

來源:互聯網
上載者:User

今天在工作上遇到這麼個需求:需要擷取對象上所有屬性的值,但並事先並不知道對象的類型。 我的第一反應就是使用反射,但是這個操作會進行多次,大量的反射肯定會有效能影響。雖然對我這個項目無關緊要,但我還是選擇了另外一種解決方案:構建運算式樹狀架構,再產生委託,然後將委託緩衝在字典裡。代碼如下:

首先構建運算式樹狀架構(類似這種形式:'(a) => a.xx'),並產生委託:

 

 代碼如下 複製代碼

private static Delegate BuildDynamicGetPropertyValueDelegate(PropertyInfo property)
{
    var instanceExpression = Expression.Parameter(property.ReflectedType, "instance");
    var memberExpression = Expression.Property(instanceExpression, property);

    var lambdaExpression = Expression.Lambda(memberExpression, instanceExpression);
    return lambdaExpression.Compile();
}

 

接著,當需要擷取屬性的值時,先在字典裡查看是否有已經產生好的委託,有的話取出委託執行擷取屬性值。沒有則構建運算式樹狀架構產生委託,並放入字典中:

 

 代碼如下 複製代碼

private static Dictionary<PropertyInfo, Delegate> delegateCache = new Dictionary<PropertyInfo, Delegate>();

public static object GetPropertyValueUseExpression<TObject>(TObject obj, PropertyInfo property)
{        
    if (delegateCache.ContainsKey(property))
    {
        var func = (Func<TObject, object>)delegateCache[property];
        return func(obj);
    }

    var getValueDelegate = BuildDynamicGetPropertyValueDelegate(property);
    delegateCache[property] = getValueDelegate;
    return ((Func<TObject, object>)getValueDelegate)(obj);
}

 

就這麼簡單,完成之後,我想測試一下運算式樹狀架構版本和反射版本的效能差距如何,於是我又簡單實現反射版本作為測試對比:

 

 代碼如下 複製代碼
public static object GetPropertyValueUseReflection<TObject>(TObject obj, PropertyInfo propertyInfo)
{
    return propertyInfo.GetValue(obj);
}

 

接下來是兩者的測試代碼:

 

 代碼如下 複製代碼

class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Capacity { get; set; }
}

.....

int repeatTimes = 10000;
PropertyInfo property = typeof(Car).GetProperty("Make");
Car car = new Car();

Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < repeatTimes; i++)
{
    GetPropertyValueUseExpression(car, property);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, Cache in Dictionary expression used time: {1} ms", repeatTimes, stopwatch.ElapsedTicks);

stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < repeatTimes; i++)
{
    GetPropertyValueUseReflection(car, property);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, reflection used time: {1} ms", repeatTimes, stopwatch.ElapsedTicks);

 

在我的預想之中是這樣的:運算式樹狀架構版本在調用次數很少的情況下會慢於反射版本,隨著次數增多,運算式樹狀架構版本的優勢會越來越明顯。

但是測試結果卻出乎我的意料!!!

 

 

 

 

 

 

在調用次數為十萬、百萬、千萬次的情況下,兩者所用的時間差不多,而且反射版本居然還要快一些。這可讓我鬱悶不已。

鬱悶之後,我就在想是不是因為字典的原因導致兩者效能差不多,就添加了以下測試代碼:

 

 代碼如下 複製代碼
stopwatch.Reset();
stopwatch.Start();
var func = (Func<Car, object>)BuildDynamicGetPropertyValueDelegate(property);
for (int i = 0; i < repeatTimes; i++)
{
    func(car);
}
stopwatch.Stop();
Console.WriteLine("Repeated {0}, Immediate call expression used time: {1} ticks", repeatTimes, stopwatch.ElapsedTicks);

 

 

這部分測試代碼,在構建運算式樹狀架構產生委託之後,直接調用,去除了字典的影響。測試結果如下:

 

 

果不其然,去除字典之後速度快了10倍。

看來在我這種情況下使用字典緩衝委託的效果並不是太好。不知道是否有更好的方法來緩衝委託。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.