參考文章
1. 基本瞭解
1.1 Lambda表示式
演變過程
using System;
namespace lq1
{
class Program
{
public delegate void Tesk(int x);
public delegate int TeskPara(int x);
static void Main(string[] args)
{
new Program().Run();
}
public void Show(int x)
{
Console.WriteLine("Show");
}
public void Run()
{
// Lambda演變歷史
{
// .net framework 1.0/1.1
Tesk tesk = new Tesk(this.Show);
tesk.Invoke(1);
}
int i = 0;
{
// .net framework 2.0,匿名方法,增加delegate關鍵字,可以訪問區域性變數
Tesk tesk = new Tesk(delegate (int x)
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(2);
}
{
// .net framework 3.0 移除delegate關鍵字,增加 => 語法(goes to)
Tesk tesk = new Tesk((int x) =>
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(3);
}
{
// 可以省略引數型別,引數型別根據委託自動推斷
Tesk tesk = new Tesk((x) =>
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(3);
}
{
// 只有一個引數或一行程式碼時省略括號
Tesk tesk = new Tesk(x => Console.WriteLine("Show" + i));
tesk.Invoke(3);
}
{
// 省略例項委託程式碼
Tesk tesk = x => Console.WriteLine("Show" + i);
tesk.Invoke(3);
}
{
// 有返回值且有一行程式碼時,可以直接寫返回值(省略 return)
TeskPara tesk = x => x + 1;
tesk.Invoke(5);
Func<int, int> func = x => x + 2;
func.Invoke(5);
}
}
}
}
概述說明
Lambda
表示式是一個特殊的匿名函式,是一種高效的類似於函數語言程式設計的表示式,簡化開發中需要編寫的程式碼量
可以包含表示式和語句,並且可用於建立委託或表示式目錄樹型別,支援帶有可繫結到委託或表示式樹的輸入引數的內聯表示式
所有Lambda
表示式都使用Lambda
運算子=>
,運算子的左邊是輸入引數(如果有),右邊是表示式或語句塊
Lambda
表示式不能在委託鏈中進行刪(-=
)操作,因為每個表示式的名稱都不一樣
1.2 隱式型別
在C# 3.0中,引進了關鍵字叫做var
,var
允許宣告一個新變數,它的型別是從用來初始化器變數的表示式裡隱式的推斷出來的,即在宣告時,不需要給它定義型別,它會根據它的初始化器表示式來推斷出它的型別
var
本身不是型別,而是向編譯器發出一條用來推斷和分配型別的指令,因此,隱式型別也叫推斷型別,由編輯器自動根據表示式推斷出物件的最終型別
隱式型別的本地變數是強型別變數(就好像您已經宣告該型別一樣),但由編譯器確定型別
var Name = "李白";
var Age = 18;
var Interest = new List<string>{"唱歌","閱讀"}
1.3 匿名型別
概述說明
字面意思:沒有名字的型別
演變歷史
public void Starting()
{
{
// 使用 object 宣告
object model = new
{
id = 1,
name = "libai"
};
//Console.WriteLine(model.id); 強型別語言,編譯器檢測不到object型別中有id屬性
}
{
// 使用 dynamic 宣告,動態型別,避開編譯器檢查
dynamic model = new
{
id = 2,
name = "libai"
};
Console.WriteLine(model.id);
Console.WriteLine(model.age); // 執行時出錯
}
{
// 使用 var 宣告,匿名型別,自動推斷,宣告後的屬性是隻讀的
var model = new
{
id = 3,
name = "libai"
};
Console.WriteLine(model.id);
//Console.WriteLine(model.age); 編譯器檢測(自動推斷)無age欄位
}
}
1.4 擴充套件方法
概述說明
擴充套件方法:允許在不修改型別的內部程式碼的情況下為型別新增獨立的行為
擴充套件方法只能定義在 非泛型的靜態類中,使用 static
修飾,引數使用this
關鍵字 修飾要擴充套件的類。就是說擴充套件方法的第一個引數必須是this
關鍵開頭然後經跟要擴充套件的物件型別,然後是擴充套件物件在執行時的例項物件引用
擴充套件方法是一種特殊的靜態方法,可以像擴充套件型別上的例項方法一樣進行呼叫,能向現有型別“新增”方法,而無須建立新的派生型別、重新編譯或以其他方式修改原始型別
在使用時編譯器認為一個表示式要使用一個例項方法,但是沒有找到,需要檢查匯入的名稱空間和當前名稱空間裡所有的擴充套件方法,並匹配到適合的方法
示例一:簡單定義
public static class Extend
{
public static int ToInt(this int? k)
{
return k ?? 0;
}
}
示例二:簡單定義
public interface ILog
{
void log(string message,LogLevel logLevel);
}
public static class ILogExtensions
{
//記錄除錯資訊
public static void LogDebug(this ILog logger,string message)
{
if(true) //判斷日誌配置中是否允許輸入Debug型別的日誌
{
logger?.Log($"{message}",LogLevel.Debug);
}
}
}
示例三:模擬
where
方法
using System;
using System.Collections.Generic;
namespace lq2
{
class Program
{
static void Main(string[] args)
{
List<User> list = new List<User>
{
new User(){uid=1,uname="a1",age=18,gender=0 },
new User(){uid=2,uname="a2",age=28,gender=1 },
new User(){uid=3,uname="a3",age=23,gender=1 },
new User(){uid=4,uname="a4",age=18,gender=0 },
new User(){uid=5,uname="a5",age=33,gender=1 }
};
var d1 = list.MyWhere(x => x.uid > 3);
Console.WriteLine(d1.Count);
var d2 = list.MyWhere(x => x.age >= 18 && x.gender == 0);
Console.WriteLine(d2.Count);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string uname { get; set; }
public int gender { get; set; }
}
public static class Extend
{
public static List<T> MyWhere<T>(this List<T> rouse, Func<T, bool> func)
{
List<T> list = new List<T>();
foreach (var item in rouse)
{
if (func(item))
{
list.Add(item);
}
}
return list;
}
}
}
2. 表示式目錄樹Expression
2.1 簡單概述
表示式樹,Expression
(System.Linq.Expressions
),是一種資料結構體,用於儲存需要計算,運算的一種結構,這種結構可以只是儲存,而不進行運算,或者說是描述不同變數和常用之間關係的一種資料結構
表示式目錄樹以資料形式表示語言級別程式碼,資料儲存在樹形結構中,目錄樹中的每個節點都表示一個表示式,簡單的說是一種語法樹,或者說是一種資料結構
表示式目錄樹不能有語句體,不能當作方法,不能有大括號,只能有一行程式碼
2.2 宣告表示式目錄樹
第一種方式,快捷宣告,用
Lambda
宣告表示式目錄樹
示例一:普通型別
Expression<Func<int, int, int>> exp = (n, m) => n * m + 2;
示例二:實體類宣告
Expression<Func<User, bool>> lambda = x => x.age > 18;
第二種方式,手動拼裝目錄樹(原始方式),簡單示例
namespace e1
{
using System;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
// 以此表示式為例,手動拼接,實現相同作用
Expression<Func<int, int, int>> func = (x, y) => x * y + 2;
// 宣告變數表示式
ParameterExpression px = Expression.Parameter(typeof(int), "x");
ParameterExpression py = Expression.Parameter(typeof(int), "y");
// 宣告常量表示式
ConstantExpression constant = Expression.Constant(2, typeof(int));
// 宣告乘積表示式
BinaryExpression multiply = Expression.Multiply(px, py);
// 宣告相加表示式
BinaryExpression add = Expression.Add(multiply, constant);
// 宣告參數列達式
ParameterExpression[] parameters = new ParameterExpression[] { px, py };
// 生成表示式目錄樹
Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);
// 表示式目錄樹生成委託
var ifunc = exp.Compile();
Console.WriteLine("委託結果:" + func.Compile().Invoke(1, 2));
Console.WriteLine("表示式樹:" + ifunc.Invoke(1, 2));
}
}
}
2.3 表示式型別
方法 | 型別 | 描述 |
---|---|---|
Expression.Parameter(...) | ParameterExpression | 表示一個命名引數(變數)表示式 |
Expression.Constant(...) | ConstantExpression | 表示具有常量值的表示式 |
Expression.Add(...) | BinaryExpression | 表示具有(+,-,*,/ )運算的表示式 |
Expression.Property/Field(...) | MemberExpression | 表示訪問屬性或欄位 |
Expression.Call(...) | MethodCallExpression | 表示對靜態方法或例項方法的呼叫 |
Expression.Condition(...) | ConditionalExpression | 表示包含條件運算子的表示式 |
LambdaExpression | 描述一個Lambda表示式 | |
ListInitExpression | 表示包含集合初始值設定項的建構函式呼叫 | |
NewExpression | 表示建構函式呼叫 | |
NewArrayExpression | 表示建立新陣列並可能初始化改陣列的元素 | |
MemberMemberBinding | 表示初始化新建立物件的成員的成員 | |
MemberInitExpression | 表示呼叫建構函式並初始化新物件的一個或多個成員 | |
MemberAssignment | 表示初始化新建立物件的欄位或屬性 | |
InvocationExpression | 表示將委託或Lambda表示式應用於參數列達式列表的表示式 | |
TypeBinaryExpression | 表示表示式和型別之間的操作 | |
UnaryExpression | 表示包含一元運算子的表示式 |
3. 拼裝Expression
3.1 變數,常量拼裝
示例一:常量
static void Test1()
{
// lambda方式
Expression<Func<int>> func = () => 1 + 2;
// 宣告常量表示式
ConstantExpression constant1 = Expression.Constant(1, typeof(int));
ConstantExpression constant2 = Expression.Constant(2, typeof(int));
// 宣告相加表示式
BinaryExpression add = Expression.Add(constant1, constant2);
// 生成表示式目錄樹
Expression<Func<int>> exp = Expression.Lambda<Func<int>>(add);
// 表示式目錄樹生成委託
var ifunc = exp.Compile();
Console.WriteLine(func.Compile().Invoke());
Console.WriteLine(ifunc.Invoke());
}
示例二:常量+變數(2.2示例)
namespace e1
{
using System;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
// 以此表示式為例,手動拼接,實現相同作用
Expression<Func<int, int, int>> func = (x, y) => x * y + 2;
// 宣告變數表示式
ParameterExpression px = Expression.Parameter(typeof(int), "x");
ParameterExpression py = Expression.Parameter(typeof(int), "y");
// 宣告常量表示式
ConstantExpression constant = Expression.Constant(2, typeof(int));
// 宣告乘積表示式
BinaryExpression multiply = Expression.Multiply(px, py);
// 宣告相加表示式
BinaryExpression add = Expression.Add(multiply, constant);
// 宣告參數列達式
ParameterExpression[] parameters = new ParameterExpression[] { px, py };
// 生成表示式目錄樹
Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);
// 表示式目錄樹生成委託
var ifunc = exp.Compile();
Console.WriteLine("委託結果:" + func.Compile().Invoke(1, 2));
Console.WriteLine("表示式樹:" + ifunc.Invoke(1, 2));
}
}
}
3.2 變數,常量,方法拼接
示例一:特殊型別示例
namespace e1
{
using System;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Test2();
}
static void Test2()
{
// lambda方式
Expression<Func<User, bool>> func = (u) => u.uid.ToString().Equals("1");
// 宣告變數表示式
ParameterExpression x = Expression.Parameter(typeof(User), "x");
// 獲取欄位
PropertyInfo property = typeof(User).GetProperty("uid");
// 獲取方法
MethodInfo toString = typeof(int).GetMethod("ToString", new Type[] { });
MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
// 設定常量表示式
ConstantExpression constant = Expression.Constant("1");
// 訪問欄位表示式
MemberExpression propertyExp = Expression.Property(x, property);
// 呼叫方法表示式
var tostringExp = Expression.Call(propertyExp, toString, new Expression[0]);
var equalsExp = Expression.Call(tostringExp, equals, new Expression[] { constant });
// 生成表示式樹
Expression<Func<User, bool>> expression =
Expression.Lambda<Func<User, bool>>(equalsExp, new ParameterExpression[] { x });
User user = new User { uid = 5 };
Console.WriteLine(func.Compile().Invoke(user));
Console.WriteLine(expression.Compile().Invoke(user));
}
}
public class User
{
public int uid { get; set; }
}
}
4. 解析Expression
使用 ExpressionVisitor
解析表示式目錄樹,ExpressionVisitor
表示表示式樹的訪問者和重寫者
解析流程
- 通過
ExpressionVisitor
這個訪問者類 - 呼叫
Visit
入口(開始)方法解析表示式(自動根據表示式型別執行相應型別的解析方法) Lambda
會區分引數和方法體,排程(自動)到更加專業的方法中解析(需要在次呼叫入口Visit
方法)- 根據表示式的型別,排程(自動)到更加專業的方法中解析(需要在次呼叫入口
Visit
方法) - 根據舊的模式(表示式)產生一個新的表示式(如果需要重寫的話)
- 說明:解析順序是從右往左解析(如果是二元表示式)
4.1 簡單解析
示例一:常量
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<int>> expression = () => 1;
CustomVisitor visitor = new CustomVisitor();
var exp = visitor.Modify(expression);
}
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
示例二:變數+常量,算術運算(二元運算型別【兩個數操作】)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
// 1.建立表示式樹
Expression<Func<int, int>> expression = (y) => y + 2;
// 2.建立訪問類例項(使用繼承是為了演示過程)
CustomVisitor visitor = new CustomVisitor();
// 3.呼叫入口方法,呼叫入口方法後就會自動進行預設解析(如果沒有定義解析過程的話)
var exp = visitor.Modify(expression);
}
}
// 繼承訪問類,演示過程
public class CustomVisitor : ExpressionVisitor
{
// 呼叫入口方法,開始解析,自動呼叫表示式型別對應的解析方法
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 4.呼叫二元表示式解析方法
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine("VisitBinary");
// 4.1 判斷表示式操作型別
if (node.NodeType == ExpressionType.Add)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
return Expression.Subtract(left, right);
}
return base.VisitBinary(node);
}
// 4.呼叫常量表示式解析方法
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
4.2 特殊解析
示例一:屬性+常量(二元運算)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
CustomVisitor visitor = new CustomVisitor();
var exp = visitor.Modify(expression);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 二元運算型別
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine("VisitBinary");
if (node.NodeType == ExpressionType.Add)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
return Expression.Subtract(left, right);
}
return base.VisitBinary(node);
}
// 屬性型別
protected override Expression VisitMember(MemberExpression node)
{
Console.WriteLine("VisitMember");
return base.VisitMember(node);
}
// 常量型別
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
示例二:方法(如果要自定義解析處理的話需要預先知道方法名才可)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Expression<Func<User, bool>> expression = (u) => u.name.Contains("1");
CustomVisitor visitor = new CustomVisitor();
visitor.Visit(expression);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string name { get; set; }
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 方法表示式
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Console.WriteLine("VisitMethodCall:"+ node.Method.Name);
return node;
}
}
}
5. 應用:解析Expression示例
5.1 示例一:簡單示例,生成SQL
運算子擴充套件方法
using System;
using System.Linq.Expressions;
namespace e2
{
internal static class SqlOperator
{
internal static string ToSqlOperator(this ExpressionType type)
{
switch (type)
{
case (ExpressionType.AndAlso):
case (ExpressionType.And):
return "AND";
case (ExpressionType.OrElse):
case (ExpressionType.Or):
return "OR";
case (ExpressionType.Not):
return "NOT";
case (ExpressionType.NotEqual):
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case (ExpressionType.Equal):
return "=";
default:
throw new Exception("不支援該方法");
}
}
}
}
表示式樹解析類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace e2
{
public class ConditionBuilderVisitor : ExpressionVisitor
{
private Stack<string> _StringStack = new Stack<string>();
public string Condition()
{
string condition = string.Concat(this._StringStack.ToArray());
this._StringStack.Clear();
return condition;
}
// 解析 二元表示式 型別
protected override Expression VisitBinary(BinaryExpression node)
{
if (node == null) throw new ArgumentNullException("BinaryExpression");
this._StringStack.Push(")");
base.Visit(node.Right);//解析右邊
this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
base.Visit(node.Left);//解析左邊
this._StringStack.Push("(");
return node;
}
// 解析 屬性表示式 型別
protected override Expression VisitMember(MemberExpression node)
{
if (node == null) throw new ArgumentNullException("MemberExpression");
this._StringStack.Push(" [" + node.Member.Name + "] ");
return node;
}
// 解析 常量表示式 型別
protected override Expression VisitConstant(ConstantExpression node)
{
if (node == null) throw new ArgumentNullException("ConstantExpression");
this._StringStack.Push(node.Value.ToString());
return node;
}
// 解析 方法表示式 型別
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m == null) throw new ArgumentNullException("MethodCallExpression");
string format;
switch (m.Method.Name)
{
case "StartsWith":
format = "({0} LIKE '{1}%')";
break;
case "Contains":
format = "({0} LIKE '%{1}%')";
break;
case "EndsWith":
format = "({0} LIKE '%{1}')";
break;
default:
throw new NotSupportedException(m.NodeType + " is not supported!");
}
this.Visit(m.Object);
this.Visit(m.Arguments[0]);
string right = this._StringStack.Pop();
string left = this._StringStack.Pop();
this._StringStack.Push(String.Format(format, left, right));
return m;
}
}
}
呼叫執行
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
expression = (u) => u.age > 1 && u.uid < 2;
expression = (u) => u.age > 1 && (u.uid < 2 || u.age > 2);
expression = (u) => u.age > 1 && u.name.Contains("李");
ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
var exp = visitor.Visit(expression);
Console.WriteLine(visitor.Condition());
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
}
}
5.2 示例二:表示式樹連結,生成SQL
連結表示式擴充套件方法
using System;
using System.Linq.Expressions;
namespace e2
{
// 建立新表示式
internal class NewExpressionVisitor : ExpressionVisitor
{
// 遍歷表示式型別,當遇到引數型別表示式時,替換為我們自己定義的引數
public ParameterExpression _NewParameter { get; private set; }
public NewExpressionVisitor(ParameterExpression param)
{
this._NewParameter = param;
}
public Expression Replace(Expression exp)
{
return this.Visit(exp);
}
// 利用ExpressionVisitor統一引數
protected override Expression VisitParameter(ParameterExpression node)
{
return this._NewParameter;
}
}
/// <summary>
/// 合併表示式 And Or Not擴充套件
/// </summary>
public static class ExpressionExtend
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
var left = visitor.Replace(expr1.Body);// 重新生成了一個表示式目錄樹
var right = visitor.Replace(expr2.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, newParameter);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
// 建立參數列達式
ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
// 生成一個新的表示式
NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
var left = visitor.Replace(expr1.Body);
var right = visitor.Replace(expr2.Body);
var body = Expression.Or(left, right);
return Expression.Lambda<Func<T, bool>>(body, newParameter);
}
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
{
var candidateExpr = expr.Parameters[0];
var body = Expression.Not(expr.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
}
}
解析表示式類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace e2
{
public class ConditionBuilderVisitor : ExpressionVisitor
{
private Stack<string> _StringStack = new Stack<string>();
public string Condition()
{
string condition = string.Concat(this._StringStack.ToArray());
this._StringStack.Clear();
return condition;
}
// 解析 二元表示式 型別
protected override Expression VisitBinary(BinaryExpression node)
{
if (node == null) throw new ArgumentNullException("BinaryExpression");
this._StringStack.Push(")");
base.Visit(node.Right);//解析右邊
this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
base.Visit(node.Left);//解析左邊
this._StringStack.Push("(");
return node;
}
// 解析 屬性表示式 型別
protected override Expression VisitMember(MemberExpression node)
{
if (node == null) throw new ArgumentNullException("MemberExpression");
this._StringStack.Push(" [" + node.Member.Name + "] ");
return node;
}
// 解析 常量表示式 型別
protected override Expression VisitConstant(ConstantExpression node)
{
if (node == null) throw new ArgumentNullException("ConstantExpression");
this._StringStack.Push(node.Value.ToString());
return node;
}
// 解析 方法表示式 型別
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m == null) throw new ArgumentNullException("MethodCallExpression");
string format;
switch (m.Method.Name)
{
case "StartsWith":
format = "({0} LIKE '{1}%')";
break;
case "Contains":
format = "({0} LIKE '%{1}%')";
break;
case "EndsWith":
format = "({0} LIKE '%{1}')";
break;
default:
throw new NotSupportedException(m.NodeType + " is not supported!");
}
this.Visit(m.Object);
this.Visit(m.Arguments[0]);
string right = this._StringStack.Pop();
string left = this._StringStack.Pop();
this._StringStack.Push(String.Format(format, left, right));
return m;
}
}
}
呼叫執行
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
expression = expression.And(x=>x.uid>2);
expression = expression.Or(x=>x.uid>2);
expression = expression.Not();
ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
var exp = visitor.Visit(expression);
Console.WriteLine(visitor.Condition());
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string name { get; set; }
}
}
5.3 示例三:實體對映
場景;DTO
類轉換為 Model
類
方案一:手動,硬編碼,不易出錯,效率高,但太繁瑣
[HttpPost]
public IActionResult Sava(UserDTD dtd)
{
User user = new User
{
uid = dtd.id,
uname = dtd.name
};
_userBll.Sava(user);
}
方案二:使用反射,損耗高,兩個型別的屬性型別和名稱需保證一致
/// <summary>
/// 反射對映
/// </summary>
public class ReflectionMapper
{
/// <summary>
/// 實體轉換
/// </summary>
/// <typeparam name="T">傳入型別</typeparam>
/// <typeparam name="TResult">返回值型別</typeparam>
/// <param name="tIn">傳入引數</param>
/// <returns>轉換好的實體</returns>
public static TResult Trans<T, TResult>(T tIn)
{
TResult tOut = Activator.CreateInstance<TResult>();
foreach (var itemOut in tOut.GetType().GetProperties())
{
var propIn = tIn.GetType().GetProperty(itemOut.Name);
itemOut.SetValue(tOut, propIn.GetValue(tIn));
}
foreach (var itemOut in tOut.GetType().GetFields())
{
var fieldIn = tIn.GetType().GetField(itemOut.Name);
itemOut.SetValue(tOut, fieldIn.GetValue(tIn));
}
return tOut;
}
}
方案三:序列化反序列化,損耗高,兩個型別的屬性型別和名稱需保證一致
/// <summary>
/// 使用第三方序列化反序列化工具
/// </summary>
public class SerializeMapper
{
/// <summary>
/// 實體轉換
/// </summary>
public static TResult Trans<T, TResult>(T tIn)
{
return JsonConvert.DeserializeObject<TResult>(JsonConvert.SerializeObject(tIn));
}
}
方案四:表示式目錄樹 + 字典快取
/// <summary>
/// 生成表示式目錄樹 字典快取
/// </summary>
public class ExpressionMapper
{
/// <summary>
/// 字典快取--hash分佈
/// </summary>
private static Dictionary<string, object> _dic = new Dictionary<string, object>();
/// <summary>
/// 實體轉換
/// </summary>
public static TResult Trans<T, TResult>(T tIn)
{
string key = string.Format("funckey_{0}_{1}", typeof(T).FullName, typeof(TResult).FullName);
if (!_dic.ContainsKey(key))
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TResult).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TResult).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
Func<T, TResult> func = lambda.Compile(); //呼叫Compile方法將表示式轉換成委託
_dic[key] = func; //拼裝是一次性的
}
return ((Func<T, TResult>)_dic[key]).Invoke(tIn);
}
}
方案五:表示式目錄樹 + 泛型快取(泛型快取特點:為不同型別的組合去快取一個結果)
/// <summary>
/// 生成表示式目錄樹 泛型快取
/// </summary>
/// <typeparam name="T">傳入引數型別</typeparam>
/// <typeparam name="TResult">返回值型別</typeparam>
public class ExpressionGenericMapper<T, TResult>
{
/// <summary>
/// 泛型快取
/// </summary>
private static Func<T, TResult> _func = null;
/// <summary>
/// 靜態建構函式(只會被呼叫一次)
/// </summary>
static ExpressionGenericMapper()
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TResult).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TResult).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
_func = lambda.Compile();//拼裝是一次性的
}
/// <summary>
/// 實體轉換
/// </summary>
public static TResult Trans(T t)
{
return _func(t);
}
}
6. 擴充套件補充
6.1 Lambda表示式本質
通過反編譯工具得知,Lambda
表示式,其實就是一個方法,在中間語言中,為其分配了一個方法名稱(<>
)
6.2 新語法:擴充套件方法
注意事項
- 例項方法優先於擴充套件方法(允許存在同名例項方法和擴充套件方法),注意優先順序
- 可以在空引用上呼叫擴充套件方法
- 擴充套件方法必須放在一個非巢狀、非泛型的靜態類中,可以被繼承
- 至少有一個引數,第一個引數必須附加
this
關鍵字,不能有任何其他修飾符(out/ref
)
編譯結果
public static class Extend
{
public static int ToInt(this int? k)
{
return k ?? 0;
}
}
.class public auto ansi abstract sealed beforefieldinit lq1.Extend
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public hidebysig static
int32 ToInt (
valuetype [mscorlib]System.Nullable`1<int32> k
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x216c
// Code size 13 (0xd)
.maxstack 1
.locals init (
[0] int32
)
IL_0000: nop
IL_0001: ldarga.s k
IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method Extend::ToInt
} // end of class lq1.Extend
6.3 Linq To Object/Sql
linq to object
宣告的方法在 Enumerable
類中,針對於 Enumerable
進行處理,資料來之記憶體資料
操作的表示式是一個委託
inq to sql
宣告的方法在 Queryable
類中,針對於 Queryable
進行處理,資料來之記憶體資料或來自資料庫的的資料來源
操作的表示式是一個表示式目錄樹,通過表示式目錄樹解析成SQL
語句
6.4 yield
迭代器
using System;
using System.Collections.Generic;
namespace lq2
{
class Program
{
static void Main(string[] args)
{
List<User> list = new List<User>
{
new User(){uid=1,uname="a1",age=18,gender=0 },
new User(){uid=2,uname="a2",age=28,gender=1 },
new User(){uid=3,uname="a3",age=23,gender=1 },
new User(){uid=4,uname="a4",age=18,gender=0 },
new User(){uid=5,uname="a5",age=33,gender=1 }
};
var d1 = list.MyWhere(x => x.uid > 3);
foreach (var item in d1)
{
Console.WriteLine(item.uid);
}
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string uname { get; set; }
public int gender { get; set; }
}
public static class Extend
{
public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> rouse, Func<T, bool> func)
{
foreach (var item in rouse)
{
if (func(item))
{
// yield 迭代器通常與 Enumerable共同使用,實現按需獲取(延遲載入)
yield return item;
}
}
}
}
}
6.5 表示式目錄樹與委託
Expression
一般都是都是配合委託一起來使用的,比如和委託Action
,Func
Expression<Func<T>>
是可以轉成Func
的(通過compile()
方法轉換),反之則不行
6.6 ORM與表示式樹目錄的關係
平常專案中經常用到的EF
操作時的擴充套件方法(Where
之類的)其實傳的就是表示式目錄樹