Activator.CreateInstance 方法建立對象和Expression Tree建立對象效能的比較(建構函式含多參數的情況)

來源:互聯網
上載者:User
前言

標題實在是太長了,是嗎?其實並無什麼高深的內容。今天看了 Will Meng 的 《Activator.CreateInstance(Type type)方法建立對象和Expression Tree建立對象效能的比較(終結版)》一文,請沒有看過該博文的朋友先前往圍觀,或許會得到一些啟發。而我想知道如果建立對象帶參數會是什麼樣的情況呢,可惜沒有看到,於是自己動手試試,作為前文的一點補充。而本文另一個目的是,對於多參數的情況,本人提供的實現方法實在是不雅,請高手指點如何重構它們成為一個通用的版本。

Expression Tree 建立對象1. 無參數的情況
public static Func<object> CreateInstanceDelegate(this Type type){    NewExpression newExp = Expression.New(type);    Expression<Func<object>> lambdaExp =        Expression.Lambda<Func<object>>(newExp, null);    Func<object> func = lambdaExp.Compile();    return func;}

(註:以上方法是擴充方法,請放到一個static class裡)

 

測試:

Type type = typeof(Bar);var createInstance = type.CreateInstanceDelegate();for (int i = 0; i < 1000000; i++){    createInstance();}

 

好的,如果你看明白了上面的代碼,那麼接著看下去才有意義。

 

2. 多參數情況

我試圖一步到位建立以下通用的含多參數的建立對象方法:

Func<object[], object> CreateInstanceDelegate(Type type, params object[] args)

結果多次修改嘗試仍然失敗,雖然我知道失敗的原因,但不知道如何修正它。

//以下代碼有bug!public static Func<object[], object> CreateInstanceDelegate(this Type type, params  object[] args){    var construtor = type.GetConstructor(args.Select(c => c.GetType()).ToArray());    var param = buildParameters(args);    NewExpression newExp = Expression.New(construtor, param);    Expression<Func<object[], object>> lambdaExp =        Expression.Lambda<Func<object[], object>>(newExp, param);    Func<object[], object> func = lambdaExp.Compile();    return func;}static ParameterExpression[] buildParameters(object[] args){    int i = 0;    List<ParameterExpression> list = new List<ParameterExpression>();    foreach (object arg in args)    {        list.Add(Expression.Parameter(arg.GetType(), "arg" + (i++)));    }    return list.ToArray();}

(註:再次說明以上代碼有bug,不能使用)

以上方法中的Lambda運算式“Expression<Func<object[], object>> ”已經定義參數是object[], 而建構函式的參數卻不能自動轉化。當使用以下代碼作測試,

var createInstance = typeof(Bar).CreateInstanceDelegate(0, "");

 

結果報“Incorrect number of parameters supplied for lambda declaration” 錯誤。或許需要作一個運算式Convertor 就可以,不過暫時沒進一步探究。

無奈之下,我唯有先搞定一個參數的情況,

public static Func<T, object> CreateInstanceDelegate<T>(this Type type){    Type paramType = typeof(T);    var construtor = type.GetConstructor(new Type[] { paramType });    var param = new ParameterExpression[] { Expression.Parameter(paramType, "arg") };    NewExpression newExp = Expression.New(construtor, param);    Expression<Func<T, object>> lambdaExp =        Expression.Lambda<Func<T, object>>(newExp, param);    Func<T, object> func = lambdaExp.Compile();    return func;}

 

通過使用泛型,建立Lambda運算式時不會報參數不匹配的錯誤了。

這裡定義一個Bar類型作測試用,有3個建構函式:

public class Bar{    public Bar() { }    public Bar(int num) { }    public Bar(int num, string str) { }}

 

測試例子:

Type type = typeof(Bar);var createInstance = type.CreateInstanceDelegate<int>();for (int i = 0; i < count; i++){    createInstance(i);}

 

這裡列出 Activator.CreateInstanceExpression Tree 和直接使用 new 的效能測試代碼:

static void Test2(){    Console.WriteLine("Test2 - CreateInstance(帶1參數): ");    Stopwatch watcher = new Stopwatch();    Type type = typeof(Bar);    watcher.Reset();    watcher.Start();    for (int i = 0; i < count; i++)    {        Activator.CreateInstance(type, i);    }    watcher.Stop();    Console.WriteLine("Activator:  " + watcher.Elapsed);    watcher.Reset();    watcher.Start();     var createInstance = type.CreateInstanceDelegate<int>();    for (int i = 0; i < count; i++)    {        createInstance(i);    }    watcher.Stop();    Console.WriteLine("Expression: " + watcher.Elapsed);    watcher.Reset();    watcher.Start();    for (int i = 0; i < count; i++)    {        new Bar(i);    }    watcher.Stop();    Console.WriteLine("Direct:     " + watcher.Elapsed);    Console.WriteLine();}

 

測試通過,而且效能也優於Activator.CreateInstance,詳細結果請看文章末的總體測試結果

問題總結:

實際開發中,我希望得到的版本是跟Activator.CreateInstance 一樣的:

public static object CreateInstance(Type type, params object[] args);

 

那麼,得到問題一:如何來封裝以下方法

Func<T, object> CreateInstanceDelegate<T>(this Type type)

 

如何緩衝這種委託 Func<T, object> ?

使用Expression Tree建立含兩個參數的建構函式,雖然也是照葫蘆畫瓢的事情,如

public static Func<T1, T2, object> CreateInstanceDelegate<T1, T2>(this Type type){    var types = new Type[] { typeof(T1), typeof(T2) };    var construtor = type.GetConstructor(types);    int i = 0;    var param = types.Select(t => Expression.Parameter(t, "arg" + (i++))).ToArray();    NewExpression newExp = Expression.New(construtor, param);    Expression<Func<T1, T2, object>> lambdaExp =        Expression.Lambda<Func<T1, T2, object>>(newExp, param);    Func<T1, T2, object> func = lambdaExp.Compile();    return func;}

 

但是這是個好方法嗎?我想不是,那麼得到問題二: 對於任意多個參數應該怎麼寫才合理?

代碼下載:CoolCode.Linq.Expressions.rar總體測試結果:

測試次數皆是一百萬次

Test1 - CreateInstance(無參數):Activator:  00:00:00.1673892Expression: 00:00:00.0126696Direct:       00:00:00.0067769Test2 - CreateInstance(帶1參數):Activator:  00:00:02.9516177Expression: 00:00:00.0135498Direct:       00:00:00.0074452Test3 - CreateInstance(帶2參數):Activator:  00:00:03.1932820Expression: 00:00:00.0151513Direct:       00:00:00.0063228

 

以上結果顯示 Activator.CreateInstance 如果帶多個參數,速度明顯下降,而後兩者則影響不大。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.