前言
代码较多,直接搬老师的,但是感觉很有用,都是和标题密切相关的内容。要全部看完哟!
PS:部分老师打的广告去掉了,嘿嘿!
Lambda前世今生
在.NetFramewok的不同版本都有不同的样子;在.NetCore下也都是支持的;
接下来代码展示:
public class LambdaShow
{
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, string y);//1 声明委托
public delegate int WithReturnNoPara();
public delegate string WithReturnWithPara(out int x, ref int y);
public void Show()
{
//Lambda前世今生;
{
//.Netframework1.0/1.1时代
NoReturnNoPara method = new NoReturnNoPara(DoNothing);
NoReturnWithPara method1 = new NoReturnWithPara(Study);
method1.Invoke(123, "Richard");
}
//.NetFramework2.0 匿名方法 增加了一个delegate关键字,可以访问到除了参数以外的局部变量
int i = 0;
{
NoReturnWithPara method = new NoReturnWithPara(delegate (int id, string name)
{
Console.WriteLine($"{id} {name} 学习.Net高级班");
Console.WriteLine(i);
});
method.Invoke(123, "Richard");
}
//.NetFramework3.0
{
//.NetFramework3.0 去掉了delegate关键字,添加了一个=> goes to
NoReturnWithPara method = new NoReturnWithPara((int id, string name) =>
{
Console.WriteLine($"{id} {name} 学习.Net高级班");
Console.WriteLine(i);
});
method.Invoke(123, "Richard");
}
{
//.NetFramework3.0 后期,去掉了匿名方法红的参数类型
NoReturnWithPara method = new NoReturnWithPara((id, name) => //编译器自动推断来的/编译器提供的便捷功能(语法糖)
{
Console.WriteLine($"{id} {name} 学习.Net高级班");
Console.WriteLine(i);
});
method.Invoke(123, "Richard");
}
{
//如果匿名方法体中只有一行代码,可以省略方法体的大括号;
NoReturnWithPara method = new NoReturnWithPara((id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班")
);
method.Invoke(123, "Richard");
}
{
NoReturnWithPara method = (id, name) => Console.WriteLine($"{id} {name} 学习.Net高级班");
method.Invoke(123, "Richard");
}
{
//一个参数的时候;
Action<string> method = s => Console.WriteLine("欢迎大家来到.Net高级班进阶学习");
method.Invoke("牧羊人。。");
}
{
//如果有返回值? 如果lambda表达式中只有一行代码,且有返回值,可以省略return;
Func<string> func = () => "黄大仙";
Func<int, string> func1 = i => i.ToString();
}
//大家觉得Lambda表达式本质是什么?
//多播委托中是不能把Lambda表达式-=;因为Lambda表达式其实是一个方法,不同的lambda表达式就是不同的方法;
//lambda的本质是一个方法;
// 语法糖:编译器提供的便捷功能;
}
private void DoNothing()
{
Console.WriteLine("This is DoNothing");
}
private void Study(int id, string name)
{
//Console.WriteLine(i);
Console.WriteLine($"{id} {name} 学习.Net高级班");
}
}
匿名类
匿名类就是声明的时候以var类型来标识。系统会根据声明右侧的数据类型来推断数据类型。
#region 3.0出了个匿名类
{
Console.WriteLine("*****************匿名类**************");
//正常情况声明类
Student student = new Student()
{
Id = 1,
Name = "Nochat",
Age = 25,
ClassId = 2
};
student.Study();
Console.WriteLine(student.Id);
Console.WriteLine(student.Name);
//匿名类object
object model = new//3.0
{
Id = 1,
Name = "Nochat",
Age = 25,
ClassId = 2
};
//无法访问属性值
//Console.WriteLine(model.Id); //为什么? 因为Object没有这个属性;因为C#是强类型语言,object是在编译时确定类型额;
//Console.WriteLine(model.Name);
//dynamic避开编译器检查 (4.0)
//(动态类型),可以避开编译器的检查
dynamic dModel = new// dynamic可以便可编译器的检查
{
Id = 1,
Name = "Nochat",
Age = 25,
ClassId = 2
};
dModel.Id = 134;
Console.WriteLine(dModel.Id);
Console.WriteLine(dModel.Name);
//Console.WriteLine(dModel.Js);// 会报异常
//var 语法糖 可以理解为弱类型;不确定类型;
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
var model1 = new //不能声明方法
{
Id = 1,
Name = "Nochat",
Age = 25,
ClassId = 2
};
Console.WriteLine(model1.Id); //var 定义的匿名类,可以访问存在的属性值;
Console.WriteLine(model1.Name);
//Console.WriteLine(model1.Js);
// model1.Id = 3; // 属性是只读,不能设置值;
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
int i2 = 2;
var i1 = 1;//var就是个语法糖,由编译器自动推算
var s = "Richard";
//var strnull = null; //编译器不允许;
// var aa;//var声明的变量必须初始化,必须能推算出类型
//也不允许作为方法的参数类型
1 var 配合匿名类型使用
2 var 偷懒,复杂类型的使用
3 在不知道具体是什么类型的时候就可以使用var 来声明;
//缺陷:个人认为,在代码阅读的时候不是很方便;
//建议大家在写代码的时候,尽量明确类型;
var i = 13;
var j = "";
var mode = new { };
}
扩展方法
首先建立一个学生类
里边有参数有方法。
public class Student
{
public int Id { get; set; }
public int ClassId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
private string Decription { get; set; }
public void Study()
{
Console.WriteLine("{0} {1}跟着A老师学习.net高级开发", this.Id, this.Name);
}
public void StudyFramework()
{
Console.WriteLine("{0} {1}跟着B老师学习如何做一名架构师。。。", this.Id, this.Name);
}
public void StudyFullStack()
{
Console.WriteLine("{0} {1}跟着C老师学习全栈开发。。。", this.Id, this.Name);
}
}
/// <summary>
/// 班级实体
/// </summary>
public class Class
{
public int Id { get; set; }
public string ClassName { get; set; }
}
现在又有一个老师开了新课
咱们需要对student进行方法添加。1:修改student;2:添加扩展方法。
{
Student student = new Student()
{
Id = 1,
Name = "Mr.zhang",
Age = 25,
ClassId = 2
};
//如果要增加一个方法---和C老师一起学习全栈开发;
//给老师支个招:
//最直接的方法:就只直接增加一个方法;
//增加了方法,就修改了这个Student类型,修改了这个类,一定需要重新测试;就会导致代码不稳定;
//没有遵循开闭原则: 面向扩展开发,面向修改关闭;
//直接增加方法,其实就是修改了类,修改类,类就不稳定;
//正确的姿势:应该是不修改类的前提下,可以做到增加功能、这才是真正的扩展之道;
student.Study();
student.StudyFramework();
student.StudyFullStack();
MethodExtension.StudyFullStack(student);
student.StudyFullStack(); //就和调用实例方法是一样的; 这就是真正的扩展之道
//张三:直接在Student类中增加了一个方法;
//李四:写一个扩展方法
//二者会冲突吗? 虽然不会冲突,但是还是会以实例方法为准;
//建议:建议大家以后如果需要新增功能,尽量不要在之前的基础上修改,而是通过扩展来,只要是标准统一,就不会出现以上情况;
int i = 1;
int? j = 2;
//int x = i + j; //类型不一样;
int x = i + j.ToInt();
string sResult = "吧啦吧啦VIP课程学习完毕以后, 老师希望大家多多money";
// 朝夕教育高级班VIP课程.......
sResult.ToStringLength();
//能不能扩展一下Object,扩展了Object以后,任何一个类型都可以来调用;因为Objectct是所有类型的父类----扩展方法是可以被继承的;
//1. 扩展Object 类型以后,会造成类型的方法污染;
//2. 除非需求很明确,否则不要随意的扩展Object或者没有约束的泛型;
//i.ObjExtend()
string s = "";
s.ObjExtend();
student.ObjExtend();
s.GenericExtend();
student.GenericExtend();
i.GenericExtend();
}
扩展方法的添加
/// 扩展方法:
/// 1.增加一个静态类
/// 2.定义一个静态方法 把需要扩展的类传递过来
/// 3.在新增的方法中的第一个参数 标记一个this 关键字
public static class MethodExtension
{
/// <summary>
/// 扩展方法其实是新增的一个方法
/// </summary>
/// <param name="student"></param>
public static void StudyFullStack(this Student student)
{
//student.Decription;
Console.WriteLine("{0} {1}跟着C老师学习全栈开发。。。", student.Id, student.Name);
}
public static int ToInt(this int? i)
{
return i ?? 0; //这里??的意思是i是否为空,为空返回0,否则返回i
//if (i == null)
//{
// return 0;
//}
//else
//{
// return i.Value;
//}
}
/// <summary>
/// /截取字符串扩展
/// </summary>
/// <param name="str"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string ToStringLength(this string str,int length=10)
{
if (string.IsNullOrWhiteSpace(str))
{
return string.Empty;
}
else if (str.Length>length)
{
return $"{str.Substring(0, length)}.....";
}
else
{
return str;
}
}
public static string ObjExtend(this object obj)
{
if (obj == null)
{
return string.Empty;
}
//.......
//else if (true)
//{
//}
else
{
return obj.ToString();
}
}
public static string GenericExtend<T>(this T t)
{
if (t == null)
{
return string.Empty;
}
//.......
//else if (true)
//{
//}
else
{
return t.ToString();
}
}
}
//如果要扩展属性或者扩展字段:可以通过继承来实现;
Linq
在这里要说一下Linq和Lambda的区别
Linq:是一个扩展方法。
Lambda:是一个匿名方法。
/// <summary>
/// Linq To Object
/// .NetFramework3.0的一个非常重大的改变
/// </summary>
public class LinqShow
{
#region Data Init
private List<Student> GetStudentList()
{
#region 初始化数据
List<Student> studentList = new List<Student>()
{
new Student()
{
Id=1,
Name="赵亮",
ClassId=2,
Age=35
},
new Student()
{
Id=1,
Name="再努力一点",
ClassId=2,
Age=23
},
new Student()
{
Id=1,
Name="王炸",
ClassId=2,
Age=27
},
new Student()
{
Id=1,
Name="疯子科学家",
ClassId=2,
Age=26
}
};
#endregion
return studentList;
}
#endregion
public void Show()
{
List<Student> studentList = this.GetStudentList();
//常规情况下数据过滤
//1.要求查询Student中年龄小于30的;
{
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Age < 30)
{
list.Add(item);
}
}
//循环完毕以后,list里面必然是符合要求的数据;
//Func<Student, bool> func = item => item.Age < 30;
var list1 = MethodExtension.RichardWhere(studentList, item => item.Age < 30);
var list2 = studentList.RichardWhere(item => item.Age < 30);
}
//2.要求Student名称长度大于2
{
//Name长度大于2的
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Name.Length > 2)
{
list.Add(item);
}
}
var list1 = MethodExtension.RichardWhere(studentList, item => item.Name.Length > 2);
var list2 = studentList.RichardWhere(item => item.Name.Length > 2);
//循环完毕以后,list里面必然是符合要求的数据;
}
//3. N个条件叠加
{
//N个条件叠加
var list = new List<Student>();
foreach (var item in studentList)
{
if (item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20)
{
list.Add(item);
}
}
var list1 = MethodExtension.RichardWhere(studentList, item => item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20);
//循环完毕以后,list里面必然是符合要求的数据;
var list2 = studentList.RichardWhere<Student>(item => item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20);
}
//思考一下:如果来一堆Teache,那我要过滤这个Teacher 怎么办呢?
//需要共用行,需要一个方法满足不同类型的需求---那就是泛型方法;
///1.为什么不喜欢前面的这种循环呢? 不好看,代码量多--其实代码多不是事儿---封装一下;
///2.随着条件的变更,每一次都需要来写一个循环;都是循环---主要问题是:相同代码太多了;
#region Linq 扩展方法&表达式
{
var list = studentList.Where<Student>(s => s.Age < 30); //list里面必然是符合要求的数据;
foreach (var item in list)
{
Console.WriteLine("Name={0} Age={1}", item.Name, item.Age);
}
}
{
Console.WriteLine("********************");
var list = from s in studentList
where s.Age < 30
select s; //list里面必然是符合要求的数据;
foreach (var item in list)
{
Console.WriteLine("Name={0} Age={1}", item.Name, item.Age);
}
}
#endregion
#region linq to object Show
{
Console.WriteLine("********************");
var list = studentList.Where<Student>(s => s.Age < 30)
.Select(s => new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
});
foreach (var item in list)
{
Console.WriteLine("Name={0} Age={1}", item.ClassName, item.IdName);
}
}
{
Console.WriteLine("********************");
var list = from s in studentList
where s.Age < 30
select new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
};
foreach (var item in list)
{
Console.WriteLine("Name={0} Age={1}", item.ClassName, item.IdName);
}
}
{
Console.WriteLine("********************");
var list = studentList.Where<Student>(s => s.Age < 30)//条件过滤
.Select(s => new//投影
{
Id = s.Id,
ClassId = s.ClassId,
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高级班" : "其他班"
})
.OrderBy(s => s.Id)//排序
.OrderByDescending(s => s.ClassId)//倒排
.Skip(2)//跳过几条
.Take(3)//获取几条
;
foreach (var item in list)
{
Console.WriteLine($"Name={item.ClassName} Age={item.IdName}");
}
}
{//group by
Console.WriteLine("********************");
var list = from s in studentList
where s.Age < 30
group s by s.ClassId into sg
select new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
};
foreach (var item in list)
{
Console.WriteLine($"key={item.key} maxAge={item.maxAge}");
}
//group by new {s.ClassId,s.Age}
//group by new {A=s.ClassId>1}
}
{
Console.WriteLine("********************");
var list = studentList.GroupBy(s => s.ClassId).Select(sg => new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
});
foreach (var item in list)
{
Console.WriteLine($"key={item.key} maxAge={item.maxAge}");
}
}
List<Class> classList = new List<Class>()
{
new Class()
{
Id=1,
ClassName="初级班"
},
new Class()
{
Id=2,
ClassName="高级班"
},
new Class()
{
Id=3,
ClassName="微信小程序"
},
};
{
var list = from s in studentList
join c in classList on s.ClassId equals c.Id
select new
{
Name = s.Name,
CalssName = c.ClassName
};
foreach (var item in list)
{
Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
}
}
{
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
});
foreach (var item in list)
{
Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
}
}
{//左连接
var list = from s in studentList
join c in classList on s.ClassId equals c.Id
into scList
from sc in scList.DefaultIfEmpty()//
select new
{
Name = s.Name,
CalssName = sc == null ? "无班级" : sc.ClassName//c变sc,为空则用
};
foreach (var item in list)
{
Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
}
Console.WriteLine(list.Count());
}
{
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
}).DefaultIfEmpty();//为空就没有了
foreach (var item in list)
{
Console.WriteLine($"Name={item.Name},CalssName={item.CalssName}");
}
Console.WriteLine(list.Count());
}
{
}
#endregion
}
}
接下来编写扩展方法中的内容。用来实现跟Lambda中一样的功能。也是上述类中所调用的。
public static class MethodExtension
{
/// <summary>
/// 这就是Linq中where的本质;
/// 可以让开发者只需要关注自己的核心业务,其他别都以Linq封装;
///
/// Linq---Linq To Object:就是针对于IEnumerable类型数据;
/// IEnumerable类型数据:可以理解成内存中的数据;
/// 1.其实就是把不变的逻辑封装起来,把可变的逻辑封装成委托来传递;
///
/// 延伸一下:
/// Linq To Sql:就可以把打开数据库连接,查询数据(不变的),把Sql的拼装(可变的逻辑);
/// Linq To Xml:把数据解析这类动作封装起来(不变的逻辑),筛选条件封装成委托传递;
/// Linq To Redis: 把固定逻辑封装起来,可变的逻辑封装成委托传递;
/// Linq To Cache:...........
/// Linq To JSON:...
/// Linq To EveryThing:......
///
/// 现在大家觉得Linq是什么? Linq是一种封装思想:
///
/// IEnumerable类型数据:可以理解成内存数据---来自于数据库的数据;把固定逻辑封装起来,不变的逻辑封装成委托传递;这是一种伟大的封装思想:对于用户来说,就不用关心内部怎么实现,只需要Linq一下即可;微软之前很推崇这种思想,只让开发者关注自己的核心逻辑,彻底的把开发者变成小白; 如果大家有经验,你们会发现像AspNetMVC就属于是傻瓜式开发;成套的;不用去关心原理,就可以直接上手;
///
///但是现在又变了。。。现在有更高的要求,像LinqToJSON,Linq To Redis。。。。并没有人来封装这些; 如果大家有兴趣,可以去尝试去封装一下;老师看同学们作业完成情况;如果完成的,我就同意评讲;如果相对较少,我就不讲了;
///
///IQueryable类型数据:可以是内存数据,数据库的数据;
///
/// Linq的常用操作:
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static List<T> RichardWhere<T>(this List<T> resource, Func<T, bool> func) where T:class
{
List<T> list = new List<T>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
public static IEnumerable<T> RichardWhereEnumerable<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
{
List<T> list = new List<T>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
/// <summary>
/// yield要和IEnumerable<T>配合使用,可以做到按需获取;
/// 找到一个就使用一个;
/// 否则继续往后找;
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static IEnumerable<T> RichardWhereIterator<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
{
foreach (var item in resource)
{
Console.WriteLine("yield开始判断。。。");
if (func.Invoke(item))
{
yield return item;
}
}
}
/// <summary>
/// 如果换个条件怎么办?
///
/// 使用委托:委托可以把方法当做参数传递;方法其实是逻辑,委托可以把逻辑当做参数传递;
///
/// 委托:应该是返回值为bool的委托,参数是一个Student
/// </summary>
/// <param name="resource"></param>
/// <returns></returns>
public static List<Student> RichardWhere(this List<Student> resource, Func<Student, bool> func)
{
List<Student> list = new List<Student>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //条件的判断其实就是逻辑---动作 //唯一的区别就在与中间这里的条件不一样;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
}