最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#中方法的直接調(diào)用、反射調(diào)用與Lambda表達式調(diào)用對比

 更新時間:2015年06月01日 10:00:20   投稿:junjie  
這篇文章主要介紹了C#中方法的直接調(diào)用、反射調(diào)用與Lambda表達式調(diào)用對比,本文著重講解了方法的三種調(diào)用方法以及它們的性能對比,需要的朋友可以參考下

想調(diào)用一個方法很容易,直接代碼調(diào)用就行,這人人都會。其次呢,還可以使用反射。不過通過反射調(diào)用的性能會遠遠低于直接調(diào)用——至少從絕對時間上來看的確是這樣。雖然這是個眾所周知的現(xiàn)象,我們還是來寫個程序來驗證一下。比如我們現(xiàn)在新建一個Console應(yīng)用程序,編寫一個最簡單的Call方法。

復(fù)制代碼 代碼如下:

class Program
{
    static void Main(string[] args)
    {
       
    }

    public void Call(object o1, object o2, object o3) { }
}


Call方法接受三個object參數(shù)卻沒有任何實現(xiàn),這樣我們就可以讓測試專注于方法調(diào)用,而并非方法實現(xiàn)本身。于是我們開始編寫測試代碼,比較一下方法的直接調(diào)用與反射調(diào)用的性能差距:
復(fù)制代碼 代碼如下:

static void Main(string[] args)
{
    int times = 1000000;
    Program program = new Program();
    object[] parameters = new object[] { new object(), new object(), new object() };
    program.Call(null, null, null); // force JIT-compile

    Stopwatch watch1 = new Stopwatch();
    watch1.Start();
    for (int i = 0; i < times; i++)
    {
        program.Call(parameters[0], parameters[1], parameters[2]);
    }
    watch1.Stop();
    Console.WriteLine(watch1.Elapsed + " (Directly invoke)");

    MethodInfo methodInfo = typeof(Program).GetMethod("Call");
    Stopwatch watch2 = new Stopwatch();
    watch2.Start();
    for (int i = 0; i < times; i++)
    {
        methodInfo.Invoke(program, parameters);
    }
    watch2.Stop();
    Console.WriteLine(watch2.Elapsed + " (Reflection invoke)");

    Console.WriteLine("Press any key to continue...");
    Console.ReadKey();
}


執(zhí)行結(jié)果如下:
復(fù)制代碼 代碼如下:

00:00:00.0119041 (Directly invoke)
00:00:04.5527141 (Reflection invoke)
Press any key to continue...

通過各調(diào)用一百萬次所花時間來看,兩者在性能上具有數(shù)量級的差距。因此,很多框架在必須利用到反射的場景中,都會設(shè)法使用一些較高級的替代方案來改善性能。例如,使用CodeDom生成代碼并動態(tài)編譯,或者使用Emit來直接編寫IL。不過自從.NET 3.5發(fā)布了Expression相關(guān)的新特性,我們在以上的情況下又有了更方便并直觀的解決方案。

了解Expression相關(guān)特性的朋友可能知道,System.Linq.Expressions.Expression<TDelegate>類型的對象在調(diào)用了它了Compile方法之后將得到一個TDelegate類型的委托對象,而調(diào)用一個委托對象與直接調(diào)用一個方法的性能開銷相差無幾。那么對于上面的情況,我們又該得到什么樣的Delegate對象呢?為了使解決方案足夠通用,我們必須將各種簽名的方法統(tǒng)一至同樣的委托類型中,如下:

復(fù)制代碼 代碼如下:

public Func<object, object[], object> GetVoidDelegate()
{
    Expression<Action<object, object[]>> exp = (instance, parameters) =>
        ((Program)instance).Call(parameters[0], parameters[1], parameters[2]);

    Action<object, object[]> action = exp.Compile();
    return (instance, parameters) =>
    {
        action(instance, parameters);
        return null;
    };
}


如上,我們就得到了一個Func<object, object[], object>類型的委托,這意味它接受一個object類型與object[]類型的參數(shù),以及返回一個object類型的結(jié)果——等等,朋友們有沒有發(fā)現(xiàn),這個簽名與MethodInfo類型的Invoke方法完全一致?不過可喜可賀的是,我們現(xiàn)在調(diào)用這個委托的性能遠高于通過反射來調(diào)用了。那么對于有返回值的方法呢?那構(gòu)造一個委托對象就更方便了:
復(fù)制代碼 代碼如下:

public int Call(object o1, object o2) { return 0; }

public Func<object, object[], object> GetDelegate()
{
    Expression<Func<object, object[], object>> exp = (instance, parameters) =>
        ((Program)instance).Call(parameters[0], parameters[1]);

    return exp.Compile();
}


至此,我想朋友們也已經(jīng)能夠輕松得出調(diào)用靜態(tài)方法的委托構(gòu)造方式了??梢?,這個解決方案的關(guān)鍵在于構(gòu)造一個合適的Expression<TDelegate>,那么我們現(xiàn)在就來編寫一個DynamicExecuter類來作為一個較為完整的解決方案:

復(fù)制代碼 代碼如下:

public class DynamicMethodExecutor
{
    private Func<object, object[], object> m_execute;

    public DynamicMethodExecutor(MethodInfo methodInfo)
    {
        this.m_execute = this.GetExecuteDelegate(methodInfo);
    }

    public object Execute(object instance, object[] parameters)
    {
        return this.m_execute(instance, parameters);
    }

    private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
    {
        // parameters to execute
        ParameterExpression instanceParameter =
            Expression.Parameter(typeof(object), "instance");
        ParameterExpression parametersParameter =
            Expression.Parameter(typeof(object[]), "parameters");

        // build parameter list
        List<Expression> parameterExpressions = new List<Expression>();
        ParameterInfo[] paramInfos = methodInfo.GetParameters();
        for (int i = 0; i < paramInfos.Length; i++)
        {
            // (Ti)parameters[i]
            BinaryExpression valueObj = Expression.ArrayIndex(
                parametersParameter, Expression.Constant(i));
            UnaryExpression valueCast = Expression.Convert(
                valueObj, paramInfos[i].ParameterType);

            parameterExpressions.Add(valueCast);
        }

        // non-instance for static method, or ((TInstance)instance)
        Expression instanceCast = methodInfo.IsStatic ? null :
            Expression.Convert(instanceParameter, methodInfo.ReflectedType);

        // static invoke or ((TInstance)instance).Method
        MethodCallExpression methodCall = Expression.Call(
            instanceCast, methodInfo, parameterExpressions);
       
        // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
        if (methodCall.Type == typeof(void))
        {
            Expression<Action<object, object[]>> lambda =
                Expression.Lambda<Action<object, object[]>>(
                    methodCall, instanceParameter, parametersParameter);

            Action<object, object[]> execute = lambda.Compile();
            return (instance, parameters) =>
            {
                execute(instance, parameters);
                return null;
            };
        }
        else
        {
            UnaryExpression castMethodCall = Expression.Convert(
                methodCall, typeof(object));
            Expression<Func<object, object[], object>> lambda =
                Expression.Lambda<Func<object, object[], object>>(
                    castMethodCall, instanceParameter, parametersParameter);

            return lambda.Compile();
        }
    }
}

DynamicMethodExecutor的關(guān)鍵就在于GetExecuteDelegate方法中構(gòu)造Expression Tree的邏輯。如果您對于一個Expression Tree的結(jié)構(gòu)不太了解的話,不妨嘗試一下使用Expression Tree Visualizer 來對一個現(xiàn)成的Expression Tree進行觀察和分析。我們將一個MethodInfo對象傳入DynamicMethodExecutor的構(gòu)造函數(shù)之后,就能將各組不同的實例對象和參數(shù)對象數(shù)組傳入Execute進行執(zhí)行。這一切就像使用反射來進行調(diào)用一般,不過它的性能就有了明顯的提高。例如我們添加更多的測試代碼:

復(fù)制代碼 代碼如下:

DynamicMethodExecutor executor = new DynamicMethodExecutor(methodInfo);
Stopwatch watch3 = new Stopwatch();
watch3.Start();
for (int i = 0; i < times; i++)
{
    executor.Execute(program, parameters);
}
watch3.Stop();
Console.WriteLine(watch3.Elapsed + " (Dynamic executor)");

現(xiàn)在的執(zhí)行結(jié)果則是:

復(fù)制代碼 代碼如下:

00:00:00.0125539 (Directly invoke)
00:00:04.5349626 (Reflection invoke)
00:00:00.0322555 (Dynamic executor)
Press any key to continue...

事實上,Expression<TDelegate>類型的Compile方法正是使用Emit來生成委托對象。不過現(xiàn)在我們已經(jīng)無需將目光放在更低端的IL上,只要使用高端的API來進行Expression Tree的構(gòu)造,這無疑是一種進步。不過這種方法也有一定局限性,例如我們只能對公有方法進行調(diào)用,并且包含out/ref參數(shù)的方法,或者除了方法外的其他類型成員,我們就無法如上例般愜意地編寫代碼了。

補充

木野狐兄在評論中引用了Code Project的文章《A General Fast Method Invoker》,其中通過Emit構(gòu)建了FastInvokeHandler委托對象(其簽名與Func<object, object[], object>完全相同)的調(diào)用效率似乎較“方法直接”調(diào)用的性能更高(雖然從原文示例看來并非如此)。事實上FastInvokeHandler其內(nèi)部實現(xiàn)與DynamicMethodExecutor完全相同,居然有如此令人不可思議的表現(xiàn)實在讓人嘖嘖稱奇。我猜測,F(xiàn)astInvokeHandler與DynamicMethodExecutor的性能優(yōu)勢可能體現(xiàn)在以下幾個方面:

1.范型委托類型的執(zhí)行性能較非范型委托類型略低(求證)。
2.多了一次Execute方法調(diào)用,損失部分性能。
3.生成的IL代碼更為短小緊湊。
4.木野狐兄沒有使用Release模式編譯。:P

不知道是否有對此感興趣的朋友能夠再做一個測試,不過請注意此類性能測試一定需要在Release編譯下進行(這點很容易被忽視),否則意義其實不大。

此外,我還想強調(diào)的就是,本篇文章進行是純技術(shù)上的比較,并非在引導大家追求點滴性能上的優(yōu)化。有時候看到一些關(guān)于比較for或foreach性能優(yōu)劣的文章讓許多朋友都糾結(jié)與此,甚至搞得面紅耳赤,我總會覺得有些無可奈何。其實從理論上來說,提高性能的方式有許許多多,記得當時在大學里學習Introduction to Computer System這門課時得一個作業(yè)就是為一段C程序作性能優(yōu)化,當時用到不少手段,例如內(nèi)聯(lián)方法調(diào)用以減少CPU指令調(diào)用次數(shù)、調(diào)整循環(huán)嵌套順序以提高CPU緩存命中率,將一些代碼使用內(nèi)嵌ASM替換等等,可謂“無所不用其極”,大家都在為幾個時鐘周期的性能提高而發(fā)奮圖強歡呼雀躍……

那是理論,是在學習。但是在實際運用中,我們還必須正確對待學到的理論知識。我經(jīng)常說的一句話是:“任何應(yīng)用程序都會有其性能瓶頸,只有從性能瓶頸著手才能做到事半功倍的結(jié)果?!崩纾胀╓eb應(yīng)用的性能瓶頸往往在外部IO(尤其是數(shù)據(jù)庫讀寫),要真正提高性能必須從此入手(例如數(shù)據(jù)庫調(diào)優(yōu),更好的緩存設(shè)計)。正因如此,開發(fā)一個高性能的Web應(yīng)用程序的關(guān)鍵不會在語言或語言運行環(huán)境上,.NET、RoR、PHP、Java等等在這一領(lǐng)域都表現(xiàn)良好。

相關(guān)文章

  • C#與java TCP通道加密通信實例

    C#與java TCP通道加密通信實例

    這篇文章主要介紹了C#與java TCP通道加密通信,文章通過真實的案例詳細講解說明C#與java TCP通道加密通信的相關(guān)資料,需要的小伙伴可以參考一下
    2021-10-10
  • WPF換膚設(shè)計原理淺析

    WPF換膚設(shè)計原理淺析

    這篇文章主要為大家詳細介紹了WPF換膚設(shè)計原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • C#實現(xiàn)順序表(線性表)完整實例

    C#實現(xiàn)順序表(線性表)完整實例

    這篇文章主要介紹了C#實現(xiàn)順序表(線性表)的方法,結(jié)合完整實例形式分析了順序表的原理及C#相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2016-06-06
  • 一起來學習C#的觀察者模式

    一起來學習C#的觀察者模式

    這篇文章主要為大家詳細介紹了C#的觀察者模式,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 快速學習C# 設(shè)計模式之職責鏈模式

    快速學習C# 設(shè)計模式之職責鏈模式

    這篇文章主要介紹了C# 設(shè)計模式之職責鏈模式的的相關(guān)資料,文中代碼非常細致,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-06-06
  • 在 C# 中使用 插值字符串

    在 C# 中使用 插值字符串

    這篇文章主要介紹了在 C# 中使用 插值字符串,字符串插值是一種將 表達式 插入到字符串字面量中的一種技術(shù),又稱為變量替換,變量插值,變量展開 等等,它是一種用相應(yīng)值替換字符串中的一個或者更多個占位符的處理過程
    2022-01-01
  • C#實現(xiàn)拷貝文件到另一個文件夾下

    C#實現(xiàn)拷貝文件到另一個文件夾下

    這篇文章主要介紹了C#實現(xiàn)拷貝文件到另一個文件夾下,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • C#中Lambda表達式的三種寫法

    C#中Lambda表達式的三種寫法

    這篇文章介紹了C#中Lambda表達式的三種寫法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • C#之線程同步Mutex類方式

    C#之線程同步Mutex類方式

    這篇文章主要介紹了C#之線程同步Mutex類方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • C#連接SQLite數(shù)據(jù)庫并實現(xiàn)基本操作

    C#連接SQLite數(shù)據(jù)庫并實現(xiàn)基本操作

    本文介紹了SQLite,一個輕量級的跨平臺數(shù)據(jù)庫管理系統(tǒng),以及如何在C#中使用System.Data.SQLite庫進行操作,包括創(chuàng)建、修改和查詢數(shù)據(jù)庫,以及使用SQLiteHelper類簡化SQL使用,此外,還提到了DB文件查看工具SQLiteSpy的應(yīng)用,需要的朋友可以參考下
    2024-12-12

最新評論

凌海市| 额尔古纳市| 济南市| 化德县| 铅山县| 舟曲县| 石城县| 井冈山市| 鄢陵县| 宣城市| 平乡县| 湟源县| 怀集县| 津南区| 平阴县| 扶绥县| 金平| 昭苏县| 姜堰市| 湛江市| 阳新县| 神农架林区| 汝城县| 布尔津县| 彭州市| 弥勒县| 阿克陶县| 龙胜| 岐山县| 常熟市| 婺源县| 佛教| 登封市| 陈巴尔虎旗| 堆龙德庆县| 上饶市| 雷山县| 镇坪县| 正蓝旗| 山阴县| 永靖县|