C# 6.0本质论(支持标准查询操作符的集合接口)

十四、支持标准查询操作符的集合接口

14.1 匿名类型与隐式变量

  • 隐式局部变量 var
    • 只能用于局部变量
    • 不可以作为方法的参数类型
  • 匿名类型
    • 由编译器声明的类型
      • new { key1=value1, key2=value2 }
      • 不是没有类型,而是编译器根据具体的值自动生成类型class
    • 不可变性
      • 一旦实例化,属性或字段都不可变
    • 类型兼容
      • 属性名称、数据类型和属性顺序都必须完全匹配
      • 相同属性,属性顺序不同也会生成不同的匿名类
      • 类型兼容的,使用相同的类进行实例化
    • ToString()等方法被重写
      • 按{ key1=value1, key2=value2 }的形式输出

14.2 集合初始化器

14.2.1 集合初始化器

  • 类似数组声明,在构造函数调用的后面添加大括号
    • 由编译器调用集合的Add()方法
    • 条件
      • 只要集合中定义了Add()方法
        • 例如,实现ICollection<T>接口
        • 或者在实现IEnumerable<T>接口的类型的基础上有Add()方法

14.2.2 字典类型的初始化

  • C#6.0
    • new Directory<key,value> { [key1]=value1, [key2]=value2 }
  • C#6.0之前
    • new Directory<key,value> { {key1,value1}, {key2,value2} }

14.2.3 初始化匿名类型数组

var A = new[]
{
	new
	{ 
		key1=value1,
		key2=value2
	}, 
	new
	{
		key1=value1,
		key2=value2
	}
}

14.3 集合接口IEnumerable<T>

14.3.1 数组的foreach

  • 在CIL中也是通过for实现的
  • 需要知道数组的长度以及索引操作符的支持,即length-index模式

在这里插入图片描述

14.3.2 集合遍历

  • 迭代器模式
    • 确定第一个元素、下一个元素和最后一个元素
    • 不需要事先知道元素总数,也不需要按照索引获取元素
  • IEnumerator、IEnumerator<T>
    • MoveNext()
    • Current:只读属性
    • 问题
      • 交错:foreach嵌套和多线程循环
      • 错误处理:循环中出现异常
  • IEnumerable、IEnumerable<T>
    • GetEnumerator()
      • 返回一个实现了IEnumerator接口的类实例,一般是集合的内部嵌套类
      • 每个实例维护循环遍历的状态
    • IEnumerator<T>派生于IDisposable,需要实现Dispose()方法释放保存的状态
  • Duck Typing
    • If it walks like a duck and quacks like a duck, it must be a duck
    • 集合不一定要实现IEnumerable接口,只需该集合具备GetEnumerator()方法,并返回带有MoveNext()和Current类型的对象就行
  • 集合遍历是不能对集合进行增、删、改操作

14.4 标准查询操作符

  • Linq
    • Language Integrated Query

14.4.1 System.Linq.Enumerable类

  • IEnumerable<T> 接口拓展了很多方法
  • 每个方法都是标准查询操作符,用于从集合中筛选数据

14.4.2 AsParallel()

  • System.Linq.ParallelEnumerable拓展方法
  • 集合遍历与执行表达式操作并行运行

14.4.3 Count()

  • Count()
    • 可以接收Func<T, bool>作为参数,对满足条件的元素计数
    • 会遍历集合,尽量避免
    • 判断集合是否有元素应使用Any()
  • 集合的Count属性
    • 如果集合实现了ICollection<T>接口,优先使用Count属性,会调用内建的Count机制

14.4.4 推迟执行

  • 将Lambda表达式赋值时,Lambda表达式不会立即执行
  • 只有在遍历集合时Lambda表达式才被执行

14.4.5 ToXXX()

  • 会遍历集合,并会返回一个新的结果缓存在内存中
  • 利用缓存对象进行操作可以不涉及查询表达式

14.4.6 OrderBy()、ThenBy()

  • OrderBy()
    • 返回IOrderedEnumerable<T>接口,该接口派生于IEnumerable<T>
  • ThenBy()
    • 额外排序条件
    • System.Linq.Extensions.Enumerable中拓展IOrderedEnumerable<T>接口的
    • 执行后续的排序操作时会重复调用之前的keySelector Lambda表达式
  • OrderByDescending()、ThenByDescending()

14.4.7 Join()

  • 将两个集合进行内联,例如,A.Join( B, keyA, keyB, ( A, B ) => new { result } )
  • 匿名类型用于生成返回结果

14.4.8 GroupBy()

  • 对一个集合进行分组,结果数等于组数,例如,A.GroupBy( keySelector )
  • 返回 IGrouping<TKey,TElement> 类型的集合
    • 每个 IGrouping<TKey,TElement> 是根据keySelector得到的一个分组集合
    • IGrouping<TKey,TElement> 派生于IEnumerable<T>
      • 该接口有一个属性指定了分组依据的键
    • 可以通过 foreach ( TElement in IGrouping) 遍历element
    • 一个IGrouping是一个组,该组有一个键,多个element

14.4.9 GroupJoin()

  • 分组联接
    • 先分组,再根据组的键值进行联接
    • 一对多
      • Lambda表达式对应的委托类型是Func<T, IEnumerable<T>, TResult>
      • 联接不匹配时返回空集合,相当于左外联接
var items = departments.GroupJoin(
	employees,
	department=>department.Id,
	employee=>employee.DepartmentId,
	(department, departmentEmployees)=>new
	{
		department.Id,
		department.Name,
		Employees=departmentEmployees
	});
foreach(var item in items)
{
	Console.WriteLine(item.Name);
	foreach(Emplyee employee in item.Employees)
	{
		Console.WriteLine(employee);
	}
}

14.4.10 SelectMany()

  • 接收参数
    • Func<TSource, IEnumerable<TResult>>
      • 用于将集合中的集合汇总到一个集合
      • 例如,teams数组中,每一个team都有一个包含球员的数组,SelectMany()返回所有球队的球员集合

14.4.11 DefaultIfEmpty()

  • 集合查询结果若为空,则返回该类型的默认值
  • 括号中也可指定其默认值

14.4.12 其他API方法

在这里插入图片描述
在这里插入图片描述

14.4.13 IQuerable<T>

  • 派生自IEnumerable<T>接口,但不继承其拓展方法
  • System.Linq.Queable类为IQuerable<T>接口拓展了类似方法
  • 通过IQuerable<T>接口可以实现自定义的LINQ Provider,为将Lambda表达式转换为其他语言如SQL提供一种解释机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值