表達式樹練習實踐:C#值類型、引用類型、泛型、集合、調用函數

来源:https://www.cnblogs.com/whuanle/archive/2019/09/22/11569083.html
-Advertisement-
Play Games

表達式樹練習實踐:C 值類型、引用類型、泛型、集合、調用函數 [TOC] 一,定義變數 C 表達式樹中,定義一個變數,使用 。 創建變數結點的方法有兩種, 兩種方式都是生成 類型 和 都具有兩個重載。他們創建一個 ParameterExpression節點,該節點可用於標識表達式樹中的參數或變數。 ...


目錄

表達式樹練習實踐:C#值類型、引用類型、泛型、集合、調用函數

img

一,定義變數

C# 表達式樹中,定義一個變數,使用 ParameterExpression

創建變數結點的方法有兩種,

Expression.Parameter()
Expression.Variable()
// 另外,定義一個常量可以使用 Expression.Constant()。

兩種方式都是生成 ParameterExpression 類型 Parameter()Variable() 都具有兩個重載。他們創建一個 ParameterExpression節點,該節點可用於標識表達式樹中的參數或變數。

對於使用定義:

Expression.Variable 用於在塊內聲明局部變數。

Expression.Parameter用於聲明輸入值的參數。

先看第一種

        public static ParameterExpression Parameter(Type type)
        {
            return Parameter(type, name: null);
        }
        
                public static ParameterExpression Variable(Type type)
        {
            return Variable(type, name: null);
        }

從代碼來看,沒有區別。

再看看具有兩個參數的重載

        public static ParameterExpression Parameter(Type type, string name)
        {
            Validate(type, allowByRef: true);
            bool byref = type.IsByRef;
            if (byref)
            {
                type = type.GetElementType();
            }

            return ParameterExpression.Make(type, name, byref);
        }
        public static ParameterExpression Variable(Type type, string name)
        {
            Validate(type, allowByRef: false);
            return ParameterExpression.Make(type, name, isByRef: false);
        }

如你所見,兩者只有一個 allowByRef 出現了區別,Paramter 允許 Ref, Variable 不允許。

筆者在官方文檔和其他作者文章上,都沒有找到具體區別是啥,去 stackoverflow 搜索和查看源代碼後,確定他們的區別在於 Variable 不能使用 ref 類型。

從字面意思來看,聲明一個變數,應該用Expression.Variable, 函數的傳入參數應該使用Expression.Parameter

無論值類型還是引用類型,都是這樣子定義。

二,訪問變數/類型的屬性欄位和方法

訪問變數或類型的屬性,使用

Expression.Property()

訪問變數/類型的屬性或欄位,使用

Expression.PropertyOrField()

訪問變數或類型的方法,使用

Expression.Call()

訪問屬性欄位和方法

Expression.MakeMemberAccess

他們都返回一個 MemberExpression類型。

使用上,根據實例化/不實例化,有個小區別,上面說了變數或類型。

意思是,已經定義的值類型或實例化的引用類型,是變數;

類型,就是指引用類型,不需要實例化的靜態類型或者靜態屬性欄位/方法。

上面的解釋不太嚴謹,下麵示例會慢慢解釋。

1. 訪問屬性

使用 Expression.Property()Expression.PropertyOrField()調用屬性。

調用靜態類型屬性

Console 是一個靜態類型,Console.Title 可以獲取編譯器程式的實際位置。

            Console.WriteLine(Console.Title);

使用表達式樹表達如下

            MemberExpression member = Expression.Property(null, typeof(Console).GetProperty("Title"));
            Expression<Func<string>> lambda = Expression.Lambda<Func<string>>(member);

            string result = lambda.Compile()();
            Console.WriteLine(result);

            Console.ReadKey();

因為調用的是靜態類型的屬性,所以第一個參數為空。

第二個參數是一個 PropertyInfo 類型。

調用實例屬性/欄位

C#代碼如下

            List<int> a = new List<int>() { 1, 2, 3 };
            int result = a.Count;
            Console.WriteLine(result);
            Console.ReadKey();

在表達式樹,調用實例的屬性

            ParameterExpression a = Expression.Parameter(typeof(List<int>), "a");
            MemberExpression member = Expression.Property(a, "Count");

            Expression<Func<List<int>, int>> lambda = Expression.Lambda<Func<List<int>, int>>(member, a);
            int result = lambda.Compile()(new List<int> { 1, 2, 3 });
            Console.WriteLine(result);

            Console.ReadKey();

除了 Expression.Property() ,其他的方式請自行測試,這裡不再贅述。

2. 調用函數

使用 Expression.Call() 可以調用一個靜態類型的函數或者實例的函數。

調用靜態類型的函數

以 Console 為例,調用 WriteLine() 方法

            Console.WriteLine("調用WriteLine方法");

            MethodCallExpression method = Expression.Call(
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
                Expression.Constant("調用WriteLine方法"));

            Expression<Action> lambda = Expression.Lambda<Action>(method);
            lambda.Compile()();
            Console.ReadKey();

Expression.Call() 的重載方法比較多,常用的重載方法是

public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)

因為要調用靜態類型的函數,所以第一個 instance 為空(instance英文意思是實例)。

第二個 method 是要調用的重載方法。

最後一個 arguments 是傳入的參數。

調用實例的函數

寫一個類

    public class Test
    {
        public void Print(string info)
        {
            Console.WriteLine(info);
        }
    }

調用實例的 Printf() 方法

            Test test = new Test();
            test.Print("列印出來");
            Console.ReadKey();

表達式表達如下

            ParameterExpression a = Expression.Variable(typeof(Test), "test");

            MethodCallExpression method = Expression.Call(
                a,
                typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
                Expression.Constant("列印出來")
                );

            Expression<Action<Test>> lambda = Expression.Lambda<Action<Test>>(method,a);
            lambda.Compile()(new Test());
            Console.ReadKey();

註意的是,Expression.Variable(typeof(Test), "test"); 僅定義了一個變數,還沒有初始化/賦值。對於引用類型來說,需要實例化。

上面的方式,是通過外界實例化傳入裡面的,後面會說如何在表達式內實例化。

三,實例化引用類型

引用類型的實例化,使用 new ,然後選擇調用合適的構造函數、設置屬性的值。

那麼,根據上面的步驟,我們分開討論。

new

使用 Expression.New()來調用一個類型的構造函數。

他有五個重載,有兩種常用重載:

 public static NewExpression New(ConstructorInfo constructor);
 public static NewExpression New(Type type);

依然使用上面的 Test 類型

            NewExpression newA = Expression.New(typeof(Test));

預設沒有參數的構造函數,或者只有一個構造函數,像上面這樣調用。

如果像指定一個構造函數,可以

            NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));

這裡就不詳細說了。

給屬性賦值

實例化一個構造函數的同時,可以給屬性賦值。

        public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings);

        public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);

兩種重載是一樣的。

我們將 Test 類改成

    public class Test
    {
        public int sample { get; set; }
        public void Print(string info)
        {
            Console.WriteLine(info);
        }
    }

然後

            var binding = Expression.Bind(
                typeof(Test).GetMember("sample")[0],
                Expression.Constant(10)
            );

創建引用類型

Expression.MemberInit()

表示調用構造函數並初始化新對象的一個或多個成員。

如果實例化一個類,可以使用

            NewExpression newA = Expression.New(typeof(Test));
            MemberInitExpression test = Expression.MemberInit(newA,
                new List<MemberBinding>() { }
                );

如果要在實例化時給成員賦值

            NewExpression newA = Expression.New(typeof(Test));

            // 給 Test 類型的一個成員賦值
            var binding = Expression.Bind(
                typeof(Test).GetMember("sample")[0],Expression.Constant(10));

            MemberInitExpression test = Expression.MemberInit(newA,
                new List<MemberBinding>() { binding}
                );

示例

實例化一個類型,調用構造函數、給成員賦值,示例代碼如下

            // 調用構造函數
            NewExpression newA = Expression.New(typeof(Test));

            // 給 Test 類型的一個成員賦值
            var binding = Expression.Bind(
                typeof(Test).GetMember("sample")[0], Expression.Constant(10));

            // 實例化一個類型
            MemberInitExpression test = Expression.MemberInit(newA,
                new List<MemberBinding>() { binding }
                );

            // 調用方法
            MethodCallExpression method1 = Expression.Call(
                test,
                typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
                Expression.Constant("列印出來")
                );

            // 調用屬性
            MemberExpression method2 = Expression.Property(test, "sample");

            Expression<Action> lambda1 = Expression.Lambda<Action>(method1);
            lambda1.Compile()();

            Expression<Func<int>> lambda2 = Expression.Lambda<Func<int>>(method2);
            int sample = lambda2.Compile()();
            Console.WriteLine(sample);

            Console.ReadKey();

四,實例化泛型類型於調用

將 Test 類,改成這樣

    public class Test<T>
    {
        public void Print<T>(T info)
        {
            Console.WriteLine(info);
        }
    }

Test 類已經是一個泛型類,表達式實例化示例

        static void Main(string[] args)
        {
            RunExpression<string>();
            Console.ReadKey();
        }
        public static void RunExpression<T>()
        {
            // 調用構造函數
            NewExpression newA = Expression.New(typeof(Test<T>));

            // 實例化一個類型
            MemberInitExpression test = Expression.MemberInit(newA,
                new List<MemberBinding>() { }
                );

            // 調用方法
            MethodCallExpression method = Expression.Call(
                test,
                typeof(Test<T>).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }),
                Expression.Constant("列印出來")
                );

            Expression<Action> lambda1 = Expression.Lambda<Action>(method);
            lambda1.Compile()();

            Console.ReadKey();
        }

五,定義集合變數、初始化、添加元素

集合類型使用 ListInitExpression表示。

創建集合類型,需要使用到

ElementInit 表示 IEnumerable集合的單個元素的初始值設定項。

ListInit 初始化一個集合。

C# 中,集合都實現了 IEnumerable,集合都具有 Add 扥方法或屬性。

使用 C# 初始化一個集合併且添加元素,可以這樣

            List<string> list = new List<string>()
            {
                "a",
                "b"
            };
            list.Add("666");

而在表達式樹裡面,是通過 ElementInit 調用 Add 方法初始化/添加元素的。

示例

            MethodInfo listAdd = typeof(List<string>).GetMethod("Add");

            /*
             * new List<string>()
             * {
             *     "a",
             *     "b"
             * };
             */
            ElementInit add1 = Expression.ElementInit(
                listAdd,
                Expression.Constant("a"),
                Expression.Constant("b")
                );
            // Add("666")
            ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("666"));

示例

            MethodInfo listAdd = typeof(List<string>).GetMethod("Add");

            ElementInit add1 = Expression.ElementInit(listAdd, Expression.Constant("a"));
            ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("b"));
            ElementInit add3 = Expression.ElementInit(listAdd, Expression.Constant("666"));

            NewExpression list = Expression.New(typeof(List<string>));

            // 初始化值
            ListInitExpression setList = Expression.ListInit(
                list,
                add1,
                add2,
                add3
                );
            // 沒啥執行的,就這樣看看輸出的信息
            Console.WriteLine(setList.ToString());

            MemberExpression member = Expression.Property(setList, "Count");

            Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(member);
            int result = lambda.Compile()();
            Console.WriteLine(result);

            Console.ReadKey();

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

-Advertisement-
Play Games
更多相關文章
  • JConsole(可視化工具) 運行 JConsole記憶體監控 測試代碼 這裡看到我們有倆個線程。 JPS(JVM Process status) JPS是使用的頻率最高的工具,和linux下的ps命令差不多(把J去掉就是一個ps)。 因為我這裡開啟了一個eclipse,所以運行結果如下所示: 如果 ...
  • 1. 概述 在本教程中,我們將探討如何使用兩種不同的策略改進客戶端重試:指數後退和抖動。 2. 重試 在分散式系統中,多個組件之間的網路通信隨時可能發生故障。 客戶端應用程式通過實現重試來處理這些失敗。 設想我們有一個調用遠程服務的客戶端應用程式—— PingPongService 。 如果 Pin ...
  • 0922自我總結 DJango錯誤日誌生成 setting.py設置 exception.py(拋錯設置) logging.py ...
  • 工作了兩個月了體會到了很多之前做外包小項目沒有的東西,不得不說大廠的還是有自己一套的完善的體制,不會像B站那樣泄露自己整個後臺的源碼這種事情發生。 電腦辦公 比如說在使用電腦辦公這方面,剛入職那天每個人都會領一臺電腦(MAC和Windows都有看工作需要),每個人領的電腦上都裝有 度管家 ,度管家是 ...
  • 1、ssh免密登錄 ssh ip地址 免密登錄配置 生成公鑰和私鑰 將公鑰拷貝到要免密登錄的目標機器上 .ssh文件夾下(~/.ssh)的文件功能解釋 (1)known_hosts :記錄ssh訪問過電腦的公鑰(public key) (2)id_rsa :生成的私鑰 (3)id_rsa.pub ...
  • 1、HttpClient遠程介面調用 1)用戶註冊 註冊按鈕 提交表單時,要 表單 js提交表單 2)載入外部資源文件 app.properties 編輯 RestApiServerInfo.java 3)HTTPClient工具遠程調用介面 導入依賴 發送post請求 工具類 遠程介面返回 封裝對 ...
  • 緩存的實現 我們不是做第三方比如Redis等的緩存實現,而是根據實際情況,基於C#上做一些環境變數的保存,方便項目使用。 1、系統全局變數 很多時候,在系統運行開始,需要對系統的運行參數進行保存,以便供全局使用。 代碼如下: 這裡使用一個靜態變數的Dictionary來進行保存,所有項目均可以直接獲 ...
  • 就像是.NET Framework WebApi與.NET Core WebApi一樣,.NET Framework MVC與.NET Core MVC的區別,也是框架的之間的區別。本系列先首先從.NET Framework MVC介紹,後面再去介紹.NET Core MVC 狹義MVC: MVC是 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...