Expression表達式目錄樹

来源:https://www.cnblogs.com/chenxi001/archive/2020/03/17/12509741.html
-Advertisement-
Play Games

一、初識Expression 源碼 1、在上一篇我們講到了委托(忘記了可以在看看,點贊在看養成習慣),今天要講的Expression也和委托有一點點關係吧(沒有直接關係,只是想要大家看看我其他的文章),Expression是.NET準備為Linq to Sql準備的,它的命名空間是System.Li ...


一、初識Expression

       源碼

       1、在上一篇我們講到了委托(忘記了可以在看看,點贊在看養成習慣),今天要講的Expression也和委托有一點點關係吧(沒有直接關係,只是想要大家看看我其他的文章),Expression是.NET準備為Linq to Sql準備的,它的命名空間是System.Linq.Expressions

       2、不知道大家有沒有用戶ORM(對象映射實體)的數據訪問層框架,使用過的小伙伴我相信對下麵的偽代碼不會陌生,我們在Where中傳入的就是Expression<Func<TSource, bool>> predicate

       3、我們進入Expression一看究竟,我們可以看到Expression<Func<TSource, bool>>裡面有一些方法(後面會慢慢道來),最終繼承LambdaExpression

       4、我們繼續進入LambdaExpression,我們看到了一些屬性(這些就是我們lambda的組成的方法和屬性),但是最終還是看到繼承了Expression

 

   5、繼續一鼓作氣進入Expression,到這裡我們看到了最終的基類它裡面也有很多方法,要說的話這兩天都說不完,我們就簡單的介紹一些常用的

 

 

 二、循序漸進

       1、大家可能看了上面還有一點點蒙,不急我們繼續,我們看下麵的實際操作,我們可以看到我們創建一個Expression和一個委托,我們使用Compile方法可以將Expression轉換成委托,最後我們執行的結果是一樣的。(大家是不是覺得,Expression和一個委托差不多呢?哈哈答案肯定不是)

{
                //這裡我們看這著和委托差不多,但是它還真不是委托
                Expression<Func<int, int>> expression = x => x + 10;
                //Compile方法可以將Expression轉換成委托
                Func<int, int> func = expression.Compile();
                //直接聲明委托
                Func<int, int> func1 = x => x + 10;
                Console.WriteLine("轉換之後的委托--" + func.Invoke(5));
                Console.WriteLine("委托--" + func1.Invoke(5));
            }
View Code

 

   2、接下來我們進一步的解析我們直接使用lambda表達式創建Expression<Func<int, int, int>> expression = (m, n) => m * n + 3;  然後我們在使用底層代碼實現這句代碼,我們也可以很清楚的看到這裡我們一步一步的拆解,裡面使用了Expression中一些對象創建的

 //下麵我們使用原始的方式創建一個Expression<Func<int, int, int>>

                //創建一個m參數 這裡的參數是值的(m,n)的,如果說你有幾個參數就創建幾個
                ParameterExpression parameter = Expression.Parameter(typeof(int), "m");

                //創建一個n參數
                ParameterExpression parameter1 = Expression.Parameter(typeof(int), "n");

                //創建一個常量3
                ConstantExpression constant = Expression.Constant(3, typeof(int));

                //首先算出最左邊的m*n的結果
                BinaryExpression binaryExpression = Expression.Multiply(parameter, parameter1);

                //然後算出(m*n)+3的結果
                binaryExpression = Expression.Add(binaryExpression, constant);

                //將上面分解的步驟拼接成lambda
                Expression<Func<int, int, int>> expression1 = Expression.Lambda<Func<int, int, int>>(binaryExpression, new ParameterExpression[]
                {
                    parameter,
                    parameter1
                });
                Console.WriteLine("lambda表達式方式--" + expression.Compile()(5, 6));
                Console.WriteLine("自己寫的組裝" + expression1.Compile()(5, 6));
View Code

        3、如果你覺得,還不夠我們就寫幾個實例Expression<Func<Student, bool>> expression = x => x.ID.Equals(15); 

 //首先還是定義一個x參數對於上面的x的參數
                ParameterExpression parameter = Expression.Parameter(typeof(Student), "x");
                //首先我們還是從左邊進行拆分 獲取到屬性
                MemberExpression property = Expression.Property(parameter, typeof(Student).GetProperty("ID"));
                //獲取我們的方法
                MethodInfo equals = typeof(Student).GetMethod("Equals");
                //定義我們的常量
                ConstantExpression constant = Expression.Constant("15", typeof(string));
                //定義一個方法拼接、第一個參數是我們的屬性,第二個參數是使用的方法,第三個參數是傳入方法的參數
                MethodCallExpression coll = Expression.Call(property, equals, new Expression[] { constant });
                //所有的數據解析完了之後,我們就需要將參數、方法進行拼裝了
                Expression<Func<Student, bool>> expression1 = Expression.Lambda<Func<Student, bool>>(coll, new ParameterExpression[] {
                parameter
                });
                Student student = new Student
                {
                    ID = 15
                };
                Console.WriteLine("lambda表達式方式--" + expression.Compile()(student));
                Console.WriteLine("自己組裝方式--" + expression1.Compile()(student));
View Code

        4、我們可以看出Expression就是進行圖下的不斷拆解,然後在進行組裝lambda執行

 

三、漸入佳境

  1、我記得我之前在寫AutoMapper的時候說要給大家寫一次,這次我就滿足大家,我們在寫Mode和Entity轉換的時候,量少的時候我們會直接寫硬編碼

Student student = new Student
                {
                    ID = 15,
                    Name = "產品粑粑",
                    Age = 18
                };
                //硬編碼
                {
                    //硬編碼轉換
                    StudentModel studentModel = new StudentModel
                    {
                        ID = student.ID,
                        Name = student.Name,
                        Age = student.Age
                    };
                }
View Code

     2、但是我們項目中使用的次數過於頻繁後,我們就會使用AutoMapper自動映射了,今天我們就不使用它了我們決定自己造輪子,我們分別使用(1,反射的方式、2,表達式目錄樹+字典、3,表達式目錄樹+泛型委托)

StudentModel studentModel = new StudentModel();
                    Type type1 = student.GetType();
                    Type type2 = studentModel.GetType();
                    foreach (var item in type2.GetProperties())
                    {
                        //判斷是不是存在
                        if (type1.GetProperty(item.Name) != null)
                        {
                            item.SetValue(studentModel, type1.GetProperty(item.Name).GetValue(student));
                        }
                    }
View Code
    /// <summary>
    /// 詞典方法推展
    /// </summary>
    public class DictionariesExpand<T, TOut>
    {
        /// <summary>
        /// 創建一個靜態的容器存放委托
        /// </summary>
        private static Dictionary<string, Func<T, TOut>> pairs = new Dictionary<string, Func<T, TOut>>();

        /// <summary>
        /// 轉換對象
        /// </summary>
        /// <typeparam name="T">輸入對象</typeparam>
        /// <param name="obj">輸入參數</param>
        /// <returns></returns>
        public static TOut ToObj(T obj)
        {
            //生成
            string key = typeof(T).FullName + typeof(TOut).FullName;
            if (!pairs.ContainsKey(key))
            {
                //首先我們還是創建一個參數
                ParameterExpression parameter = Expression.Parameter(typeof(T));
                //獲取要轉化後的類型
                Type type = typeof(TOut);
                //創建一個容器存放解析的成員
                List<MemberBinding> list = new List<MemberBinding>();
                //遍歷屬性
                foreach (var item in type.GetProperties())
                {
                    //獲取參數中item.Name對應的名稱
                    MemberExpression memberExpression = Expression.Property(parameter, typeof(T).GetProperty(item.Name));
                    //判斷是否存在
                    if (memberExpression != null)
                    {
                        MemberBinding member = Expression.Bind(item, memberExpression);
                        list.Add(member);
                    }
                }
                //遍歷欄位
                foreach (var item in type.GetFields())
                {
                    //獲取參數中item.Name對應的名稱
                    MemberExpression memberExpression = Expression.Field(parameter, typeof(T).GetField(item.Name));
                    //判斷是否存在
                    if (memberExpression != null)
                    {
                        MemberBinding member = Expression.Bind(item, memberExpression);
                        list.Add(member);
                    }
                }
                //初始化轉換後的類型,並且進行初始化賦值
                MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list);
                //所有的準備工作已經完成準備生成lambda
                Expression<Func<T, TOut>> expression = Expression.Lambda<Func<T, TOut>>(memberInit, new ParameterExpression[] {
                parameter
                });
                Func<T, TOut> entrust = expression.Compile();
                //生成委托存放到我們的字典
                pairs.Add(key, entrust);
                return entrust.Invoke(obj);
            }
            return pairs[key].Invoke(obj);
        }
    }
View Code
/// <summary>
    /// 泛型方法推展
    /// 當我們使用靜態方法,會執行靜態的無參構造函數 ,不會調用無參構造函數
    /// 我們使用泛型的時候會保存不同泛型的副本,一直保存在記憶體裡面不會釋放,所以可以
    /// 實現偽硬編碼
    /// </summary>
    public class GenericityExpand<T, TOut>
    {
        private static Func<T, TOut> _Func = null;
        static GenericityExpand()
        {
            //首先我們還是創建一個參數
            ParameterExpression parameter = Expression.Parameter(typeof(T));
            //獲取要轉化後的類型
            Type type = typeof(TOut);
            //創建一個容器存放解析的成員
            List<MemberBinding> list = new List<MemberBinding>();
            //遍歷屬性
            foreach (var item in type.GetProperties())
            {
                //獲取參數中item.Name對應的名稱
                MemberExpression memberExpression = Expression.Property(parameter, typeof(T).GetProperty(item.Name));
                //判斷是否存在
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //遍歷欄位
            foreach (var item in type.GetFields())
            {
                //獲取參數中item.Name對應的名稱
                MemberExpression memberExpression = Expression.Field(parameter, typeof(T).GetField(item.Name));
                //判斷是否存在
                if (memberExpression != null)
                {
                    MemberBinding member = Expression.Bind(item, memberExpression);
                    list.Add(member);
                }
            }
            //初始化轉換後的類型,並且進行初始化賦值
            MemberInitExpression memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), list);
            //所有的準備工作已經完成準備生成lambda
            Expression<Func<T, TOut>> expression = Expression.Lambda<Func<T, TOut>>(memberInit, new ParameterExpression[] {
                parameter
                });
            //生成委托存放到我們的泛型委托中
            _Func = expression.Compile();
        }

        public static TOut ToObj(T obj)
        {
            return _Func(obj);
        }
    }
View Code

       3、我針對上面的代碼,進行了迴圈百萬次的測試

         1. 直接硬編碼的形式:速度最快(0.126s)
    2. 通過反射遍歷屬性的形式 (6.328s)
    3. 利用序列化和反序列化的形式:將複製實體序列化字元串,在把該字元串反序列化被賦值實體(7.768s)
      4. 字典緩存+表達式目錄樹(Lambda的拼接代碼瞭解即可) (2.134s)
         5. 泛型緩存+表達式目錄樹(Lambda的拼接代碼瞭解即可) (0.663s)

四、總結

        1、還有一些其他的用法我還沒有完全介紹,比如可以封裝一個自己的ORM,我們使用的ORM就是通過這個進行封裝的,授人以魚不如授人以漁。在最後的一個實例中我們使用到了很多細節的知識點。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近工作比較忙,未能及時更新內容,敬請瞭解!!! 對於可視化樹的分析引出了幾個有趣問題。例如,控制項如何從邏輯樹表示擴張成可視化樹表示? 每個控制項都有一個內置的方法,用於確定如何渲染控制項(作為一組更基礎的元素)。該方法稱為控制項模板(control template),是用XAML標記塊定義的。 下麵是 ...
  • 一、AOP概念 官方解釋:AOP(Aspect-Oriented Programming,面向切麵編程),它是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程式動態統一添加功能的一種技術。它是一種新的方法論,是對傳統OOP編程的一種補充。OOP是關註將需求功能劃分為不同的並且相對獨立 ...
  • 一、基礎內容 什麼是委托? 委托的作用? (略) 自定義委托的聲明: Public Delegate [Type] Mydel() ; 顯示委托 > 匿名委托 > Lambda表達式 (略) 內置委托類型:Action<> 、Func<> 、Predicate<> (略) 二、進階內容 多播委托 多 ...
  • 參考文檔: https://www.cnblogs.com/yaopengfei/p/12418227.html https://blog.csdn.net/weixin_42694286/article/details/92974535 https://blog.csdn.net/qq_42815 ...
  • static void WebClientDownLoad() { string url = "http://p4.ssl.cdn.btime.com/t0167dce5a13c3da30d.jpg?size=5012x3094"; WebClient client = new WebClient( ...
  • ABP是一個開源應用程式框架,該項目是ASP.NET Boilerplate Web應用程式框架的下一代,專註於基於ASP.NET Core的Web應用程式開發,也支持開發控制台應用程式。 官方網站: "https://abp.io/" 官方文檔: "https://docs.abp.io/" 一、 ...
  • 登錄頁: 首頁 模塊管理 角色管理,角色分配 用戶管理 ...
  • 對於 .NET 開發者來說,nuget 是必不可少的程式包管理工具。相應地,大部分開發團隊都需要在內部搭建 Nuget 伺服器,以管理私有 nupkg 包。本文所使用的 Nuget 伺服器,不是微軟官方的,而是 Baget。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...