Expression表達式樹(C#)

Lambda表達式:express

1.下面舉例經過Lambda表達式建立了一個用於驗證Name的Func委託。數據結構

     //經過Lambda表達式建立一個對象的Name屬性驗證委託
     Func<SearchInfo, bool> func = x => x.Name.Equals("5");
    /// <summary>
    /// 測試類
    /// </summary>
    public class SearchInfo
    {
        public string Name { get; set; }

        public string Code { get; set; }
    }

  2.測試調用, 下面驗證 "6" 是否和 "5" 相等, 結果falseide

           //res=false
            bool res = func(new SearchInfo() { Name = "6" });

那麼問題來了, 若是我但願驗證的結果不是 "5" 而是其餘的內容: "7","9" ...等等呢? , 測試

那麼接下來封裝一個Func, 容許自定義驗證結果:spa

        public static Func<SearchInfo, bool> Func(string arr)
        {
            return x => x.Name.Equals(arr); //這一步將Equals中校驗的內容進行了參數封裝         }

測試代碼: debug

            bool f1=  Func("6")(new SearchInfo() { Name = "6" }); //true
            bool f2 = Func("7")(new SearchInfo() { Name = "6" }); //false
            bool f3 = Func("8")(new SearchInfo() { Name = "6" }); //false

如上所示, 咱們成功的把Equals中的驗證結果封裝了起來。可是 !實際應用這還不夠, 調試

若是你要校驗的不是Name 或是Code,  Equals 變成其餘 Contains呢?以下:code

﹋﹊﹋﹊﹋﹊﹋﹊﹋分割線﹊﹋﹊﹋﹊﹋﹊﹋﹊ 對象

Expression(表達式樹)blog

位於 System.Linq.Expressions 命名空間下, 下面介紹如何以上簡單的示例建立一個動態的Expression, 類型參數爲委託類型:

//
    // 摘要:
    //     將強類型化的 Lambda 表達式表示爲表達式樹形式的數據結構。 此類不能被繼承。
    //
    // 類型參數:
    //   TDelegate:
    //     該委託的類型, System.Linq.Expressions.Expression`1 表示。
    public sealed class Expression<TDelegate> : LambdaExpression
    {
        //
        // 摘要:
        //     編譯表達式樹由描述爲可執行代碼的 lambda 表達式,並生成一個委託,表示 lambda 表達式。
        //
        // 返回結果:
        //     類型的委託 TDelegate 表示所描述的已編譯的 lambda 表達式 System.Linq.Expressions.Expression`1。
        public TDelegate Compile();
        //
        // 摘要:
        //     將生成一個委託,表示 lambda 表達式。
        //
        // 參數:
        //   debugInfoGenerator:
        //     由編譯器用於將標記序列點並添加批註的本地變量的調試信息生成器。
        //
        // 返回結果:
        //     一個包含已編譯的版本的 lambda 的委託。
        public TDelegate Compile(DebugInfoGenerator debugInfoGenerator);
        //
        // 摘要:
        //     建立一個新的表達式,它相似於此表達式,但使用所提供的子級。 若是全部子級均相同,它將返回此表達式。
        //
        // 參數:
        //   body:
        //     System.Linq.Expressions.LambdaExpression.Body 結果屬性。
        //
        //   parameters:
        //     System.Linq.Expressions.LambdaExpression.Parameters 結果屬性。
        //
        // 返回結果:
        //     若是子級未更改,則爲此表達式;不然爲具備已更新子級的表達式。
        public Expression<TDelegate> Update(Expression body, IEnumerable<ParameterExpression> parameters);
        protected internal override Expression Accept(ExpressionVisitor visitor);
    }

示例(用表達式的形式):

Expression<Func<SearchInfo, bool>> lambda = x => x.Name.Equals("5");
 
bool res = lambda.Compile()(new SearchInfo() { Name = "6" }); // 結果 : false

註釋: 爲了可以瞭解 Expression<Func<SearchInfo, bool>> lambda = x => x.Name.Equals("5") 這段代碼的結構,

接下來用 ILSpy 反編譯, 查看到該代碼生成了以下: 

            ParameterExpression parameterExpression = Expression.Parameter(typeof(SearchInfo), "x"); 
            Expression<Func<SearchInfo, bool>> expression = Expression.Lambda<Func<SearchInfo, bool>>(Expression.Call(
Expression.Property(parameterExpression, (MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(get_Name()))),
(MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(Equals())),
new Expression[] { Expression.Constant("5", typeof(string)) }), new ParameterExpression[] { parameterExpression });

咱們分解以上代碼(先用數字標識):

1. ParameterExpression parameterExpression = Expression.Parameter(typeof(SearchInfo), "x");   參數表達式

2. Expression.Property(parameterExpression, (MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(get_Name())))   屬性表達式

3. new Expression[] { Expression.Constant("5", typeof(string)) } 常數表達式

4. (MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(Equals()))  方法調用表達式

5. Expression.Lambda<Func<SearchInfo, bool>>(Expression.Call( 2, 4, 3), 1);   建立Lambda表達式

接下來回到最開始的Lambda表達式, 圖解說明以上的代碼分別表明Lambda表達式的每一個部分:

理解上表達式生成的5個步驟, 接下來還原一個表達式完整的建立過程, 以下: 

            //1.建立參數表達式
            ParameterExpression parameterExpression = Expression.Parameter(typeof(SearchInfo), "x");

            //2.建立屬性表達式
            Expression proerty = Expression.Property(parameterExpression, typeof(SearchInfo).GetProperty("Name"));

            //3.建立常數表達式
            ConstantExpression constantExpression = Expression.Constant("5", typeof(string));

            //4.建立方法調用表達式
            MethodCallExpression toString = Expression.Call(
                proerty,
                typeof(SearchInfo).GetMethod("Equals"),
                 new Expression[] { constantExpression });

            //5.建立Lambda表達式
            Expression<Func<SearchInfo, bool>> lambda = Expression.Lambda<Func<SearchInfo, bool>>(toString, new ParameterExpression[]
            {
                    parameterExpression
            });

以上則是如何建立一個表達式樹, 測試調用, 以下: 

 bool res = lambda.Compile()(new SearchInfo() { Name = "55" }); // 「5」!=「55」   結果: false

注: Compile() 爲lambda的編譯, 後面則才能進行參數的傳遞

 

基於類(Class)動態生成表達式:

/// <summary>
    /// 測試類
    /// </summary>
    public class SearchInfo
    {
        public string Name { get; set; }

        public string Code { get; set; }

        public string Id { get; set; }

        public string Addr { get; set; }

        public string Res { get; set; }
    }
View Code

 

public static Func<T, bool> GenerateExpression<T>(T searchModel) where T : class, new()
        {
            List<MethodCallExpression> mcList = new List<MethodCallExpression>();
            Type type = searchModel.GetType();
            ParameterExpression parameterExpression = Expression.Parameter(type, "x");
            var pros = type.GetProperties();
            foreach (var t in pros)
            {
                var objValue = t.GetValue(searchModel, null);
                if (objValue != null)
                {
                    Expression proerty = Expression.Property(parameterExpression, t);
                    ConstantExpression constantExpression = Expression.Constant(objValue, t.PropertyType);
                    mcList.Add(Expression.Call(proerty, typeof(string).GetMethod("Contains"), new Expression[] { constantExpression }));
                }
            }

            if (mcList.Count == 0)
                return Expression.Lambda<Func<T, bool>>(Expression.Constant(true, typeof(bool)), new ParameterExpression[] { parameterExpression }).Compile();
            else
                return Expression.Lambda<Func<T, bool>>(MethodCall(mcList), new ParameterExpression[] { parameterExpression }).Compile();
        }

        public static Expression MethodCall<T>(List<T> mcList) where T : MethodCallExpression
        {
            if (mcList.Count == 1) return mcList[0];
            BinaryExpression binaryExpression = null;
            for (int i = 0; i < mcList.Count; i += 2)
            {
                if (i < mcList.Count - 1)
                {
                    BinaryExpression binary = Expression.OrElse(mcList[i], mcList[i + 1]);
                    if (binaryExpression != null)
                        binaryExpression = Expression.OrElse(binaryExpression, binary);
                    else
                        binaryExpression = binary;
                }
            }
            if (mcList.Count % 2 != 0)
                return Expression.OrElse(binaryExpression, mcList[mcList.Count - 1]);
            else
                return binaryExpression;
        }
View Code

測試代碼 : 

        static void Main(string[] args)
        {
            var func = GenerateExpression(new SearchInfo());

            List<SearchInfo> List = new List<SearchInfo>();
            List.Add(new SearchInfo() { Code = "1", Id = "1", Name = "3", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "2", Id = "2", Name = "4", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "3", Id = "3", Name = "5", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "4", Id = "4", Name = "6", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "5", Id = "5", Name = "7", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "6", Id = "6", Name = "8", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "7", Id = "7", Name = "9", Addr = "5", Res = "6" });
            List.Add(new SearchInfo() { Code = "8", Id = "8", Name = "3", Addr = "5", Res = "6" });

            var ii = List.Where(func).ToList(); //8個結果
        }
相關文章
相關標籤/搜索