构建一个Linq,需要lambam表达式,匿名类,扩展方法
lambam表达式
NoReturnWithPara method = (x, y) => Console.WriteLine(“This is DoNothing6”);
lambda表达式是个什么呢?
//只是一个方法(作用)
//实际上是一个类中类,里面的一个internal方法,然后被绑定到静态的委托类型字段
在一个多播委托加入这个lambam表达式,是删不掉的
扩展方法
静态类里面的静态方法,第一个参数类型前面加上this
/// 用途:可以不修改类,增加方法;也就是方便一点
/// 缺陷:优先调用类型的实例方法(有隐患)
/// 扩展基类型,导致任何子类都有这个方法,而且还可能被覆盖啥的
///
/// 指定类型扩展,不要对基类型 否则成本太高
///
/// 没有扩展属性
例:public static void Sing(this Student student)
{
Console.WriteLine($"{student.Name} Sing a Song");
}
这样就可以直接 Student.Sing
例2:
`///
/// 1 基于委托封装解耦,去掉重复代码,完成通用代码
/// 2 泛型,应对各种数据类型
/// 3 加迭代器,按需获取 就是只有在真的遍历时才会
///
///
///
///
///
//public static List ElevenWhere(this List source, Func func)
//{
// var list = new List();
// foreach (var item in source)
// {
// if (func.Invoke(item))
// {
// list.Add(item);
// }
// }
// return list;
//}
public static IEnumerable<T> ElevenWhere<T>(this IEnumerable<T> source, Func<T, bool> func)
{
if (source == null)
{
throw new Exception("source is null");
}
if (func == null)
{
throw new Exception("func is null");
}
foreach (var item in source)
{
if (func.Invoke(item))
{
yield return item;
}
}
}
var result = list.ElevenWhere<Student>(s =>
{
Console.WriteLine("检查数据是否满足条件");
Thread.Sleep(100);
return s.Age < 30;
});//陈述式语法
foreach (var item in result)
{
}
`
他会在foreach的时候触发进行
而一个linq语句的构成就是通过这些
var linq = list
.Where(s =>
{
Console.WriteLine(“检查数据是否满足条件”);
Thread.Sleep(100);
return s.Age < 30;
})
//.Select
.Select(s => new
{
Id = s.Id,
Name = s.Name,
Length = s.Name.Length
});
转到定义里
public static IEnumerable Where(this IEnumerable source, Func
Func
Expression
//表达式目录树:语法树,或者说是一种数据结构;可以被我们解析(学完数据结构也不知道在哪里可以用到,终于在这里看到使用的一个实例)
委托中的lambda表达式本质是方法,只能被执行,但表达式目录树可以被一层层解析
像linq to sql中的委托参数都是包着一层表达式目录树,这样才可以被解析成sql语句
int iResult1 = func.Invoke(12, 23);
int iResult2 = exp.Compile().Invoke(12, 23);//可以转换过去
经过反编译(m, n) => m * n + 2其实是这样声明出来的
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");
var multiply = Expression.Multiply(parameterExpression, parameterExpression2);
var constant = Expression.Constant(2, typeof(int));
var add = Expression.Add(multiply, constant);
Expression<Func<int, int, int>> expression =
Expression.Lambda<Func<int, int, int>>(
add,
new ParameterExpression[]
{
parameterExpression,
parameterExpression2
});
表达式目录树可以被分解,因此它是可以被拼接的,拼接后.Compile()就可以变成一个委托,所以是非常有意义的
例一,
//拼装表达式目录树
//Expression> lambda = x => x.Age > 5;
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");
Expression propertyExpression = Expression.Property(parameterExpression, typeof(People).GetProperty("Age"));
//Expression property = Expression.Field(parameterExpression, typeof(People).GetField("Id"));
ConstantExpression constantExpression = Expression.Constant(5, typeof(int));
BinaryExpression binary = Expression.GreaterThan(propertyExpression, constantExpression);//添加方法的
Expression<Func<People, bool>> lambda = Expression.Lambda<Func<People, bool>>(binary, new ParameterExpression[]
{
parameterExpression
});
bool bResult = lambda.Compile()(new People()
{
Id = 11,
Name = "Eleven",
Age = 31
});
也许应用场景有许多其他的需求,比如x => x.Id > 1与其他属性的各种组合,就可以用表达式目录树进行各种组合的拼接
例二,将一个对象转化为另一个对象,或者说一个对象的深拷贝,就可以用到表达式目录树的拼接
People people = new People()
{
Id = 11,
Name = "Eleven",
Age = 31
};
PeopleCopy peopleCopy = new PeopleCopy()
{
Id = people.Id,
Name = people.Name,
Age = people.Age
};
这种硬编码性能上是最好的,但是太不灵活
可以加反射进行改进
public static TOut Trans<TIn, TOut>(TIn tIn)
{
TOut tOut = Activator.CreateInstance<TOut>();
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;
}
虽然灵活性大大提高,但是反射的性能比较差
public class SerializeMapper
{
///
/// 序列化反序列化方式
///
///
///
public static TOut Trans<TIn, TOut>(TIn tIn)
{
return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
}
}
通过序列化反序列化也可以但是性能还不如反射
private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
///
/// 字典缓存表达式树
///
///
///
///
///
public static TOut Trans<TIn, TOut>(TIn tIn)
{
//字典key为两对象的名字,value为
string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
if (!_Dic.ContainsKey(key))
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TOut).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TOut).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
_Dic[key] = func;
}
return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
}
这个是拼接后的表达式lambam
//Expression
// new PeopleCopy()
// {
// Id = p.Id,
// Name = p.Name,
// Age = p.Age
// };
//lambda.Compile()(people);
只需要第一次的初始化,然后会缓存到静态字典中,性能也比较好,这就是动态生成硬编码
再优化的话可以使用泛型缓存
以上是通过反编译得到大概代码,然后自己进行解读拼装,除此之外,c#自带了解析目录树的类库方法
层层递归visit,将一个表达式二元二元逐层解析出来,识别表达式以及子表达式然后调用不同的虚方法
现在我们大概知道了这个ExpressionVistor的visit方法的实现
那他到底有什么用呢?
其实如果学会了表达式目录树你可以自己封装一个小orm或者小ef框架
逐步解析的过程中将所需部分替换sql语法例如下面
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("不支持该方法");
}
}
最后将一段linq表达式解析为sql语句,这就那些orm框架的原理
除此之外,我们还有想要拼接linq的时候可以借助表达式目录树
///
/// 合并表达式 And Or Not扩展
///
public static class ExpressionExtend
{
///
/// 合并表达式 expr1 AND expr2
///
///
///
///
///
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null)
return expr2;
else if (expr2 == null)
return expr1;
//return Expression.Lambda>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters);
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);
}
///
/// 合并表达式 expr1 or expr2
///
///
///
///
///
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null)
return expr2;
else if (expr2 == null)
return expr1;
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)
{
if (expr == null)
return null;
var candidateExpr = expr.Parameters[0];
var body = Expression.Not(expr.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
}