添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
所谓“ 集成 ”,指Linq并非只针对集合,它已经作用于数据库(Linq to SQL/EF)、XML文件(Linq to XML)、Web Service……针对于集合的操作属于Linq to Object。但所有的Linq使用统一的查询表达式(query expression)。 PS:个人感觉,从Linq开始,C#永远的将Java甩在身后!Java(以及其他语言 )没有Linq,勉强对标Linq的是 stream 演示用数据:
    Teacher fg = new Teacher { Name = "大飞哥", Age = 41 };
    Teacher fish = new Teacher { Name = "小鱼", Age = 28 };
    Teacher waiting = new Teacher { Name = "诚聘" };
    IEnumerable<Teacher> teachers = new List<Teacher> { fg, fish, waiting };
    Major csharp = new Major { Name = "C#", Teacher = fg };
    Major SQL = new Major { Name = "SQL", Teacher = fg };
    Major Javascript = new Major { Name = "Javascript", Teacher = fg };
    Major UI = new Major { Name = "UI", Teacher = fish };
    IEnumerable<Major> majors = new List<Major> { csharp, SQL, Javascript, UI };
    IList<Student> students = new List<Student>
        new Student{Score = 98, Name = "屿", Majors=new List<Major>{csharp,SQL } },
        new Student{Score = 86, Name = "行人", Majors=new List<Major>{Javascript, csharp, SQL} },
        new Student{Score = 78, Name = "王平", Majors=new List<Major>{csharp}},
        new Student{Score = 89, Name = "王枫", Majors=new List<Major>{Javascript, csharp, SQL,UI}},
        new Student{Score = 98, Name = "蒋宜蒙", Majors=new List<Major>{Javascript, csharp}},
public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate)
可以由任何IEnumerable<TSource>对象(比如List<Student>)调用。(#体会#:泛型和继承/多态的作用) Func<TSource, bool> predicate,代表的是过滤/筛选条件: 基于TSource(集合元素)进行运算,返回一个bool值,确定是否满足要求。 任何一个能返回bool值的、传入参数为TSource的lambda表达式都可以用作where条件(尤其是在Linq to Object中):
s => s.Name.StartsWith("王")
s => s.Name.StartsWith("王") && s.Score > 80
//甚至实际上不用s
s => true
PS:不要因为Lambda的参数是Student类型,不是int等简单类型就懵了 IEnumerable<TSource>,(惯例)Linq运算的结果可以用var声明,因为以后这种类型可能会变得越来越臃肿复杂。 然后,使用foreach循环输出:
foreach (var item in excellents)
    Console.WriteLine(item.Name);
MimicWhere()方法 Linq(to Object)的本质还是foreach。复习:面向函数中的filter函数 为了加深对上述Linq方法的理解(兼复习),我们自己来写一个Where条件的方法。
static class Mimic
    internal static IEnumerable<T> MimicWhere<T>(
        this IEnumerable<T> source, Func<T, bool> predicate)
        foreach (var item in source)
            if (predicate(item))
                yield return item;
	注意:使用yield避免额外的集合声明
	注意:尽量直接使用OrderBy()或OrderByDescending(),不要在OrderBy()之后再Reverse(),养成习惯,尤其是在后面Linq to SQL/EF时,注意会造成性能浪费。(暂时理解成:先排序再颠倒不如一次性排序)
	自定义比较
	排序是依赖于比较的,int、string等都是天然可比较的(实现了IComparable)
	演示:用“不可比较”的属性进行排序,会报错:
students.OrderByDescending(s => s.Majors);
因为.NET运行时会懵:两个List我咋比较? 如果我们有确定的比较方案,比如比较他们元素的个数,可以 首先需要自己声明一个实现IComparer的类
class MajorComparor<T> : IComparer<IList<T>>
    public int Compare([AllowNull] IList<T> x, [AllowNull] IList<T> y)
        return x.Count() - y.Count();
然后传入其对象:
students.OrderByDescending(s => s.Majors, new MajorComparor<Major>());
ThenBy() 实现先按某字段(比如Score)排序,当Score成绩相同时,再按另一个字段(比如Majors)排序的功能:
var excellents = students.OrderBy(s=>s.Score)
    .ThenBy(s => s.Majors, new MajorComparor<Major>());
不要使用:OrderBy().OrderBy(),这样前面一个OrderBy()会被后面一个OrderBy()覆盖…… ToList()/ToArray()/ToXXX等方法:将IEnumerable转换成List/数组/其他集合对象
List<Student> list = excellents.ToList();
Student[] array = excellents.ToArray();
First()/Single()/Last(),获取第一个/唯一一个/最后一个元素。 演示:如果说 不止一个元素的话,Single()会报错 一个都没有的话,First()和Last()会报错 如果不想抛异常的话,可以加后缀OrDefault:
Console.WriteLine(excellents.SingleOrDefault()?.Name);
意思是:如果没有符合条件的元素,返回该元素类型的默认值 引用类型null值复习 其他(0,false……) Sum/Count/Average/Min/Max:求和/元素个数/平均值/最小值/最大值
Console.WriteLine(excellents.Sum(s=>s.Score));
//区分Count属性,Count()方法里还能传过滤条件
Console.WriteLine(excellents.Count(/*s=>s.Name.StartsWith("王")*/));  
Console.WriteLine(excellents.Average(s=>s.Score));
Console.WriteLine(excellents.Max(s=>s.Score));
Console.WriteLine(excellents.Min(s=>s.Score));
两个原因: 保证获取的是“即时”(up-to-date)数据
//假设students(数据源)发生了变化
students.Add(new Student { Score = 65, Name = "" });
在进行多条件查询拼接时提高性能,使用最终表达式一次遍历,完成所有查询
foreach (var item in groupedMajor)
    Console.WriteLine(item.Key.GetType() + ":" + item.Key.Name);
    foreach (var i in item)
        Console.WriteLine("    " + i.GetType() + ":" + i.Name);
    Console.WriteLine();
	有时候我们需要用多个属性进行分组,这时候用匿名对象即可:  
majors.GroupBy(m => new { m.Name, m.Age });
ToDictionary() 更多的时候,我们是利用聚合函数进行统计,得到各个小组的:数量/最大/最小/和/平均值等……
foreach (var item in groupedMajor)
    Console.WriteLine(item.Key.Name + ":" + item.Count());
	但上述结果我们不直接输出,而是将其存储起来,如何操作? 
	可以用ToDictionary(),将老师(名称)做键,课程数量做值:
Dictionary<string, int> pairs = 
    groupedMajor.ToDictionary(gm => gm.Key.Name, gm => gm.Count());
然后在select的时候:
IEnumerable<Score> scores = students.Select(
    s => new Score { Name = s.Name, Value = s.Score });
还可以使用 复习:匿名类 但此时只能使用var声明变量: 其实在Linq to Object中并不必要,因为类的关联,总有一些其他办法。但我们还是应该掌握以下用法,以备日后Linq to SQL所用
public class Major
    //public Teacher Teacher { get; internal set; }
    public string Teacher { get; internal set; }
//Major csharp = new Major { Name = "C#", Teacher = fg };
Major csharp = new Major { Name = "C#", Teacher = "大飞哥" };
如何获取上述集合?这就需要用Join()连接若干个集合:
var tm = majors.Join(teachers, //majors连接teachers
    m => m.Teacher, t => t.Name, //连接依赖/比较的属性
    (m, t) => new //生成的匿名对象作为结果集合元素
        MajorName = m.Name,
        TeacherName = t.Name,
        TeacherAge = t.Age
	PS:Join方法不如Linq表达式有表现力,所以此处只简单介绍。 
	思路就是先把所有学生的所有课程取出来,将之前的Student:List<Major>(1:n)变成Student:Major(1:1),然后再进行过滤:
var result = students.SelectMany(
        s => s.Majors,              //指示取出students里的所有Major
        (s, m) => new { Student = s, MajorName = m.Name }      //组合student和major
    .Where(sm => sm.MajorName.ToLower().Contains("s"))  //在上述结果集中筛选
foreach (var item in result)
    Console.WriteLine($"{item.Student.Name}:{item.MajorName}");
形象理解:将原集合中每个元素中“横向”的转“纵向”