C#委托与lamdba表达式入门,以及Lamdba自己实现SelectMany等
?委托:
l 什么委托:
本质是方法指针
l 委托的作用:
作用:进行方法传递,方法可以当作一个参数进行传递,C#里边要传递方法需要借助委托
l 使用委托一般的步骤:
1:定义委托
public delegate 返回值 方法名称(参数);
2:委托可以关联方法
3:执行委托,就相当于执行方法
public static void Way() { Console.WriteLine("hello delegate"); } //1:定义一个无参数无返回值的委托 public delegate void DEL_Way(); static void Main(string[] args) { //2:给委托关联一个方法 DEL_Way delway = Way; //3:执行委托 delway.Invoke(); Console.ReadLine(); }
l 委托实现一个简单的定时器:
public class MyTimer { public void Start(Action way, int timespan) { while (true) { way(); System.Threading.Thread.Sleep(timespan); } } }
执行:
MyTimer mytimer = new MyTimer(); mytimer.Start(Way, 1000);
?系统委托
作用:简化委托的定义,系统已经定义好,使用的时候直接使用,不需要重复定义
l 系统委托的主要分类
Action:无返回值,有0-16个参数
Func:有返回值,有0-16个参数
Predicate:固定返回值为bool
Comparison:固定返回值为int
其实后面两个完全可以使用func替换,但是为了一目了然一看就知道返回值是什么
l 实例
Action action = Way; action(); Action<int> action2 = Square; action2(6); Func<int, int, int> func = Add; int result = func(8, 8); Console.WriteLine(result);
?Lamdba表达式
l 作用:
lamdba表达式可以简化方法的定义
l 语法:
(方法参数)=>{方法体};
例如:无参无返回值的lamdba表达式
()=>{};
又如:一个参数无返回值
(a)=>{};
在如:一个参数一个返回值
(a)=>{return a*a};
还如:2个参数1个返回值
(a,b)=>{return a+b};
l 实例:
//无参无返回值的lamdba表达式 Action action = () => { Console.WriteLine("hello lamdba"); }; action(); //一个参数无返回值 Action<int> action2 = (a) => { Console.WriteLine(a * a); }; action2(6); //一个参数一个返回值 Func<int, int> func = (a) => { return a * a; }; int square = func(8); Console.WriteLine(square); //2个参数1个返回值 Func<int, int, int> funcsum = (a, b) => { return a + b; }; int result = funcsum(3, 6); Console.WriteLine(result);
执行定时器的就可以用lamdba来简化方法:
MyTimer mytime = new MyTimer(); mytime.Start(() => { Console.WriteLine("国庆快乐"); }, 1000);
做一点简单的集合查询:
static void Main(string[] args) { List<string> fruitList = new List<string>() { "葡萄", "香蕉", "西瓜", "木瓜", "哈密瓜" }; Console.WriteLine("请输入查询名字"); string fruitName = Console.ReadLine(); var result = fruitList.Where(a => a.Contains(fruitName)); Console.ReadLine(); }
对象集合lamdba查询:
static void Main(string[] args) { List<Users> ulist = new List<Users>() { new Users() {username="李清照", age=23, address="宋" }, new Users() {username="黄月英", age=18, address="三国" }, new Users() {username="黄忠", age=109, address="三国" }, new Users() {username="黄蓉", age=27, address="射雕" } }; Console.WriteLine("请输入查询姓名"); string name = Console.ReadLine(); var result = ulist.Where(a => a.username.Contains(name)).OrderByDescending(a => a.age).ToList(); Console.ReadLine(); }
?Lamdba表达式自己实现foreach,Where,FirstOrDefault,SelectMany
自己实现一下微软提供的集合查询也算是抛砖引玉了
public static class IEnumerableExtands { public static void MyForEach<T>(this IEnumerable<T> source, Action<T> action) { foreach (T item in source) { action(item); } } //根据条件查询 public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> source, Predicate<T> action) { List<T> list = new List<T>(); foreach (T item in source) { if (action(item)) { list.Add(item); } } return list; } //根据条件查询返回第一条或者默认值 public static T MyFirstOrDefault<T>(this IEnumerable<T> source, Predicate<T> action) { T val = default(T); foreach (T item in source) { if (action(item)) { return item; } } return val; } }
效果:
实现集合查询SelectMany
自己写一下里边的原理对了解它的用法有很大的帮助,select都作用类似就是把1对多的格式处理成1对1的格式,
比如这里的把UserScoreViewModel格式处理成UserScoreViewModel2
我们要想写通用的扩展方法出来可以先写一个专门的处理UserScoreViewModel格式成UserScoreViewModel2的方法,这样做也便于我们分析和理解问题,特别是如果我们要进行授课,这种方式更能够让学生理解,一步一步的深入。
其实很简单就是两个遍历就好
/// <summary> /// 把List<UserScoreViewModel>转换成List<UserScoreViewModel2>格式 /// </summary> public List<UserScoreViewModel2> Parse(List<UserScoreViewModel> source) { List<UserScoreViewModel2> result = new List<UserScoreViewModel2>(); foreach (UserScoreViewModel item in source) { foreach (Score scoreItem in item.scoreList) { UserScoreViewModel2 uscore = new UserScoreViewModel2(); uscore.UserName = item.UserName; uscore.score = scoreItem; result.Add(uscore); } } return result; }
然后我们在去写通用的:
public static class IEnumberExtands { /// <summary> /// 自己实现MySelectMany /// </summary> /// <typeparam name="TSource">对应外面的集合源类型</typeparam> /// <typeparam name="TCollection">集合里边子集合类型</typeparam> /// <typeparam name="TResult">返回值类型</typeparam> /// <returns></returns> public static IEnumerable<TResult> MySelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) { List<TResult> resultList = new List<TResult>(); foreach (TSource tsource in source) { //通过外面传递的lamdba拿到集合 IEnumerable<TCollection> sublist = collectionSelector(tsource); foreach (TCollection item in sublist) { //通过外面传递的lamdba拿到返回对象 TResult result = resultSelector(tsource, item); resultList.Add(result); } } return resultList; } }
上面的方法没有处理为空的情况,如果外面写了DefaultIfEmpty()就容易报错,所以我们可以使用反射创建一个默认对象处理一下这种情况,不然我们就只有在外面去处理了
public static IEnumerable<TResult> MySelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) { List<TResult> userScoreViewModel2List = new List<TResult>(); foreach (TSource item in source) { foreach (TCollection score in collectionSelector(item)) { if (score == null) { //使用反射创建对象,避免为空报错,为空的时候我们就给一个默认值 TCollection collobj = Activator.CreateInstance<TCollection>(); TResult tresult = resultSelector(item, collobj); userScoreViewModel2List.Add(tresult); } else { TResult tresult = resultSelector(item, score); userScoreViewModel2List.Add(tresult); } } } return userScoreViewModel2List; }
最后这个还想说一点,我们使用通用的selectMany方法,并没有使用反射,而是通过外面传递的lamdba表达式,也就是外面传递的方法来实现这个通用的功能,从这里也可以看出来通过方法传递来实现一个功能是非常灵活的,可以写出很通用的代码。
当然我们这里是解析的集合,而不是解析的sql语句,如果是解析sql语句我们这里应该就是传递的表达式树了,ef中解析sql语句的selectMany原理后面有机会在说