1.Lambda的演变历史
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, string y);
public delegate int WithReturnNoPara();
public delegate string WithReturnWithPara(out int x, ref int y);
public void ShowLambdaHistory()
{
Console.WriteLine($"在.NetFramework1.0/1.1时代");
NoReturnNoPara method1 = new NoReturnNoPara(DoNothing);
method1.Invoke();
NoReturnWithPara method2 = new NoReturnWithPara(Study);
method2.Invoke(3, "牧舟");
Console.WriteLine();
Console.WriteLine("在.NetFramework2.0时代 —— 匿名方法");
Console.WriteLine("增加了一个delegate关键字,可以访问到除了参数以外的局部变量");
string localVar = "局部变量";
NoReturnWithPara method3 = new NoReturnWithPara(delegate (int id, string name)
{
Console.WriteLine($"参数:{id},{name}学习高级班课程, localVar = {localVar}");
});
method3.Invoke(10, "牧舟");
Console.WriteLine();
Console.WriteLine("在.NetFramework3.0时代");
Console.WriteLine("去掉了delegate关键字,添加了一个符号=>,读作“goes to”");
NoReturnWithPara method4 = new NoReturnWithPara((int id, string name) =>
{
Console.WriteLine($"参数:{id},{name}学习高级班课程, localVar = {localVar}");
});
method4.Invoke(11, "牧舟");
Console.WriteLine();
Console.WriteLine("在.NetFramework3.0时代后期");
Console.WriteLine("去掉了匿名方法中的参数类型,编译器自动推断(语法糖)");
NoReturnWithPara method5 = new NoReturnWithPara((id, name) =>
{
Console.WriteLine($"参数:{id},{name}学习高级班课程, localVar = {localVar}");
});
method5.Invoke(13, "牧舟");
Console.WriteLine();
Console.WriteLine("如果匿名方法体中,只有一行代码,可以省略方法体的大括号");
NoReturnWithPara method6 = new NoReturnWithPara((id, name) =>
Console.WriteLine($"参数:{id},{name}学习高级班课程, localVar = {localVar}"));
method6.Invoke(14, "牧舟");
Console.WriteLine("继续精进……");
NoReturnWithPara method7 = (id, name) =>
Console.WriteLine($"参数:{id},{name}学习高级班课程, localVar = {localVar}");
method7.Invoke(15, "牧舟");
Console.WriteLine();
Console.WriteLine("一个参数的委托");
Action<string> action = s => Console.WriteLine($"欢迎 {s} 同学,来到.Net高级班进阶学习");
action.Invoke("牧舟");
Console.WriteLine();
Console.WriteLine("有返回值的委托");
Console.WriteLine("如果Lambda表达式中只有一行代码,且有返回值,可以省略return");
Func<string> func1 = () => "牧舟同学,你好";
string resultFunc1 = func1.Invoke();
Console.WriteLine(resultFunc1);
Func<int, string> func2 = i => $"牧舟同学的学号是:{i}";
string resultFunc2 = func2.Invoke(17);
Console.WriteLine(resultFunc2);
}
private void DoNothing()
{
Console.WriteLine("This is DoNothing……");
}
private void Study(int id, string name)
{
Console.WriteLine($"{id} {name}学习.Net高级班课程");
}
Lambda表达式的本质是什么?
Lambda表达式的本质就是一个方法
多播委托中是不能把Lambda表达式进行-=的,因为Lambda表达式其实就是一个方法,不同的Lambda表达式对应不同的方法
Lambda表达式不是语法糖,语法糖是编辑器提供的便捷功能
2.内部类
public void ShowAnonymousClass()
{
Student student = new Student()
{
Id = 1,
Name = "牧舟",
Age = 25,
ClassId = 2
};
student.Study();
Console.WriteLine($"可以访问属性、调用方法 Id = {student.Id}, Age = {student.Age}");
object model = new
{
Id = 1,
Name = "牧舟",
Age = 25,
ClassId = 2
};
//model.Id;
Console.WriteLine("匿名类无法访问属性值等");
Console.WriteLine("匿名类为什么无法访问属性值?");
Console.WriteLine("因为object没有这个属性");
Console.WriteLine("因为C#是强类型语言,object是在编译时确定类型的");
Console.WriteLine();
Console.WriteLine("dynamic能够避开编译器检查(4.0)");
dynamic dmodel = new
{
Id = 1,
Name = "牧舟",
Age = 25,
ClassId = 2
};
Console.WriteLine("dynamic类型可以访问属性");
Console.WriteLine("但是如果访问到没有的属性时,因为已经避开了编译器检查,因此不会报错");
Console.WriteLine($"Id = {dmodel.Id}, Name = {dmodel.Name}");
Console.WriteLine();
Console.WriteLine("var语法糖,可以理解为弱类型,不确定类型");
var vmodel = new
{
Id = 1,
Name = "牧舟",
Age = 25,
ClassId = 2
};
Console.WriteLine("var类型可以访问存在的属性,也不会避开编译器检查");
Console.WriteLine("但是,var定义的匿名类,属性只读,不可设置值");
Console.WriteLine("var语法糖,右边值的类型可由编译器自动推断");
Console.WriteLine("编译器不允许var类型量赋值为null,var声明的变量必须初始化,必须能推算出类型");
Console.WriteLine("var不允许作为方法的参数类型");
Console.WriteLine($"Id = {vmodel.Id} Name = {vmodel.Name}");
}
var配合匿名类使用,在不知道具体是什么类型的时候就可以使用var来声明。
缺陷:个人认为,阅读代码不方便
建议:尽量明确类型
3.扩展方法
public void ShowExtensionMethod()
{
Student studentEx = new Student()
{
Id = 1,
Name = "牧舟",
Age = 25,
ClassId = 2
};
//如果Student要增加一个新的方法——跟着Ace老师学习全栈开发技术
//最直接的方法就是在Student类中添加一个跟Ace老师学习全栈技术的方法
studentEx.Study();
studentEx.StudyFramework();
studentEx.StudyFullStack();
//可是,增加了方法就修改了这个Student类,修改了这个类一定需要重新测试,就会导致代码不稳定
//直接在类中增加方法,没有遵循开闭原则,所谓开闭原则是指:面向扩展开放,面向修改关闭
//直接增加方法,其实就是修改了类,修改类,类就不稳定
//正确的姿势:应该是不修改类的前提下,可以做到增加功能,这才是真正的扩展之道
MethodExtension.StudyFullStackExtension(studentEx);
studentEx.StudyFullStackExtension();//跟调用实例方法一模一样,这才是扩展方法
//张三直接在Student类中增加了一个方法
//李四写一个扩展方法
//这两个方法同名,二者会冲突吗?不会
//虽然不会冲突,但还是会以实例方法为准
//如果需要新增功能,建议不要在之前的基础上修改,而是通过扩展来添加,只要是标准统一,就不会出现以上情况
Console.WriteLine();
Console.WriteLine("扩展方法应用小场景");
//1.可控值类型,如int类型扩展小方法
int i = 1;
int? j = 2;//int?表示可空类型,是一种特殊的值类型,值可以为null,初始值默认为null而不是0
//int x = i + j;
//可添加扩展方法,实现这个小功能
int x = i + j.ToInt();
Console.WriteLine($"1.可空值类型扩展小方法:{x}");
//2.截取字符串扩展小方法
string str = ".Net课程学习完成之后,希望技能上能有不小的突破,也希望薪资水平能够得到大大的提升……";
Console.WriteLine($"2.截取字符串扩展小方法:{str.ToStringLength(10)} {str.ToStringLength()}");
//3.object扩展小方法
Console.WriteLine($"{i.ObjectExtend()} {str.ObjectExtend()} {studentEx.ObjectExtend()}");
//扩展了object以后,任何一个类型都可以来调用,因为object是所有类型的父类——扩展方法可以被继承
//1.扩展object类型以后,会造成类型的方法污染
//2.除非需求很明确,否则不要随意扩展object或者扩展没有约束的泛型方法
Console.WriteLine($"{i.GenericExtend()} {str.GenericExtend()} {studentEx.GenericExtend()}");
}
public static class MethodExtension
{
public static void StudyFullStackExtension(this Student student)
{
Console.WriteLine($"扩展方法,{student.Id}号学员{student.Name}跟着Ace老师学习全栈开发");
}
public static int ToInt(this int? i)
{
//if (i == null)
//{
// return 0;
//}
//else
//{
// return i.Value;
//}
return i ?? 0;//??用于判断并赋值,先判断当前变量是否为null
//如果是就可以赋一个新值,否则直接用原值
}
public static string ToStringLength(this string str, int length = 20)
{
if (string.IsNullOrWhiteSpace(str))
{
return string.Empty;
}
else if (str.Length > length)
{
return $"{str.Substring(0, length)}";
}
else
{
return str;
}
}
public static string ObjectExtend(this object obj)
{
if(obj == null)
{
return string.Empty;
}
else
{
return obj.ToString();
}
}
public static string GenericExtend<T>(this T t)
{
if (t == null)
{
return string.Empty;
}
else
{
return t.ToString();
}
}
}
4.Linq
Linq的存在主要是对数据进行操作
Linq与Lambda的区别:
Lambda是匿名方法,而Linq是扩展方法,Linq更多的代表着是一种封装思想
在MethodExtension中发现,所有扩展方法的条件都是写死的
如果换个条件怎么办?每一个不同的条件都要重新写一个方法吗?
需要使用委托,委托可以把方法当做参数来传递,方法就是逻辑,也就说委托可以把逻辑当做参数来传递
public void ShowLinq()
{
List<Student> studentList = this.GetStudentList();
#region 常规情况下数据过滤
//1.要求查询Student中年龄小于30的
{
var list = new List<Student>();
foreach(var item in studentList)
{
if(item.Age < 30)
{
list.Add(item);
}
}
//循环完毕以后,list里面必然是符合要求的数据
Func<Student, bool> func1 = s => s.Age < 30;
var list1 = studentList.MuZhouWhere(func1);
foreach(var item in list1)
{
Console.WriteLine($"Id = {item.Id}, Name = {item.Name}, Age = {item.Age}");
}
var list2 = studentList.GenericWhere(func2);
foreach(var item in list2)
{
Console.WriteLine($"泛型版 Id = {item.Id}, Name = {item.Name}, Age = {item.Age}");
}
Func<Student, bool> func3 = s => s.Age < 30;
var list3 = studentList.MuZhouWhereIterator(func3);
foreach (var item in list3)
{
Console.WriteLine($"状态迭代版 Id = {item.Id}, Name = {item.Name}, Age = {item.Age}");
}
}
#endregion
}
/// <summary>
/// 针对于Student集合过滤查找数据
/// </summary>
/// <param name="students"></param>
/// <param name="func">这里的委托应该是返回值为bool的委托,参数是一个Student</param>
/// <returns></returns>
public static List<Student> MuZhouWhere(this List<Student> students, Func<Student,bool> func)
{
List<Student> list = new List<Student>();
foreach(var item in students)
{
//if(item.Age<30)//区别在于中间这里的条件不一样,条件的判断其实就是逻辑,即动作
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
/// <summary>
/// 这就是Linq中where的本质
///
/// 把固定不变的逻辑封装起来,把可变的逻辑封装在委托中传递过来
/// 就可以让开发者只需要管住自己的核心业务,其它别的都以Linq封装
/// Linq——Linq to object:就是针对于IEnumerable类型的数据
///
/// IEnumerable类型数据:可以理解成内存中的数据
/// 其实就是把不变的逻辑封装起来,把可变的逻辑封装成委托来传递
///
/// 延伸:
/// Linq to Sql:就可以吧打开数据库链接,查询数据(不变的),把Sql的拼装(可变的逻辑)封装起来
/// Linq to Xml:把数据解析这类动作封装起来(不变的逻辑),把筛选条件封装成委托传递
/// Linq to Redis:把固定逻辑封装起来,可变的逻辑封装成委托传递
/// Linq to Cache:……
/// Linq to JSOn:……
/// Linq to Everything:……
///
/// Linq是什么?
/// Linq是一种封装思想,把固定的逻辑封装起来,不变的逻辑封装成委托传递,这是一种伟大的封装思想
/// 对于用户来说,就不用关心内部怎么实现,只需要Linq一下即可
/// 微软之前很推崇这种思想,只让开发者关注自己的核心逻辑,彻底把开发者变成小白
/// 之前的ASP.Net MVC就属于傻瓜式开发,成套的,不用去关心原理就可以直接上手
/// 但是现在又变了,现在有更高的要求,像Linq To Json,Linq To Redis等并没有人来封装这些
///
/// IQueryable类型数据:可以理解成内存数据——来自于数据库的数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="lists"></param>
/// <param name="func"></param>
/// <returns></returns>
public static List<T> GenericWhere<T>(this List<T> lists, Func<T, bool> func)
{
List<T> list = new List<T>();
foreach(var item in lists)
{
Console.WriteLine("开始判断……");
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
/// <summary>
/// yield和IEnumerable<T>配合使用,可以做到按需获取
/// 找到一个就使用一个,否则继续往后找
///
/// 通过反编译工具可以看到,yield生成了一个特性——迭代状态机(IteratorStateMachineAttribute)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static IEnumerable<T> MuZhouWhereIterator<T>(this List<T> resource, Func<T,bool> func)
{
foreach(var item in resource)
{
Console.WriteLine("yield开始判断……");
if (func.Invoke(item))
{
yield return item;//yield要和IEnumerable<T>配对使用
}
}
}
如果要扩展属性或字段,可以通过继承来实现
public void StateMachine
{
private int state = 1;
/// <summary>
/// 核心要义:只要是找到了符合当前这个条件的,就直接返回回去了
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public string StateMachineMethod(int i)
{
if (i == state)
{
return " --- ";
}
{
i = 2;
//这里可能做一些计算
//就是以这个state为维度做判断
}
if (i == state)
{
return "高级班";
}
else
{
i = 1;
//就是另一种场景的判断,可以对i重新赋值
//然后递归调用(其实就是自己调用自己)
StateMachineMethod(i);
return " === ";
}
}
}