五、Lambda和Linq

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表达式对应不同的方法
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}");
}

3.0出了个匿名类
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 "  ===  ";
        }
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值