From 6e181771a2779100a000f7ae99304d2a721ebce1 Mon Sep 17 00:00:00 2001 From: bicijinlian <bicijinlian@163.com> Date: Sat, 31 Aug 2019 17:51:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LinqStudy.Test/Doc/LinqToObject.md | 43 +++++ LinqStudy.Test/Doc/Linq总体说明.md | 1 + LinqStudy.Test/LinqBase/Lamda表达式.md | 1 + LinqStudy.Test/LinqBase/初始化器.md | 1 + .../LinqBase/匿名类与匿名方法.md | 1 + LinqStudy.Test/LinqToObject/BaseTest.cs | 96 ++++++++++ LinqStudy.Test/LinqToObject/CreateTest.cs | 11 ++ LinqStudy.Test/LinqToObject/OtherTest.cs | 30 ++++ LinqStudy.Test/LinqToObject/ProjectiveTest.cs | 168 ++++++++++++++++++ LinqStudy/Models/Employee.cs | 16 ++ 10 files changed, 368 insertions(+) create mode 100644 LinqStudy.Test/Doc/LinqToObject.md create mode 100644 LinqStudy.Test/Doc/Linq总体说明.md create mode 100644 LinqStudy.Test/LinqBase/Lamda表达式.md create mode 100644 LinqStudy.Test/LinqBase/初始化器.md create mode 100644 LinqStudy.Test/LinqBase/匿名类与匿名方法.md create mode 100644 LinqStudy.Test/LinqToObject/BaseTest.cs create mode 100644 LinqStudy.Test/LinqToObject/CreateTest.cs create mode 100644 LinqStudy.Test/LinqToObject/OtherTest.cs create mode 100644 LinqStudy.Test/LinqToObject/ProjectiveTest.cs create mode 100644 LinqStudy/Models/Employee.cs diff --git a/LinqStudy.Test/Doc/LinqToObject.md b/LinqStudy.Test/Doc/LinqToObject.md new file mode 100644 index 0000000..b9ffe14 --- /dev/null +++ b/LinqStudy.Test/Doc/LinqToObject.md @@ -0,0 +1,43 @@ +# Linq To Object + +> Linq To Object是操作内存对象。 +> +> 使用前提:对象必须实现IEnumerable或`IEnumerable<T>`接口。 +> +> 因为IQueryable和`IQueryable<T>`也实现了IEnumerable或`IEnumerable<T>接口`,所以也可以使用Linq To Object。 +> 泛型集合均实现了`IEnumerable<T>`接口,可以直接使用。 +> 一些集合类,也提供了转换扩展方法,可以转换后使用。 +> + +## Linq To Object本质 + +> 本质是扩展方法,没有相应的 Linq Provider,这点与 Linq To SQL不同。 + +## 注意事项 + ++ 延时执行 + + > 写好操作步骤时,只是进行了“定义”,直到调用(转换方法、聚合方法会立即执行)时才会真正执行操作。这点特别注意。 + ++ 异常 + + > 当对象为null时,通常都会抛出“参数null值”异常。使用前,就确保对象不为null + + ``` csharp + [Fact] + public void Null_Test() + { + List<Person> person = null; + + //对Linq操作符而言,基本上数据源为Null时,将引发异常。 + Assert.ThrowsAny<ArgumentNullException>(() => + { + //此处引发异常 + var query = person.Where(p => p == null); + }); + } + ``` + ++ 操作结果 + + > 当操作结果没有数据项时,不是返回null,而是返回“0数据项”的IEnumerable对象。对结果**不用进行null值判断** diff --git a/LinqStudy.Test/Doc/Linq总体说明.md b/LinqStudy.Test/Doc/Linq总体说明.md new file mode 100644 index 0000000..97b38ca --- /dev/null +++ b/LinqStudy.Test/Doc/Linq总体说明.md @@ -0,0 +1 @@ +# Linq总体说明 diff --git a/LinqStudy.Test/LinqBase/Lamda表达式.md b/LinqStudy.Test/LinqBase/Lamda表达式.md new file mode 100644 index 0000000..b124f4b --- /dev/null +++ b/LinqStudy.Test/LinqBase/Lamda表达式.md @@ -0,0 +1 @@ +# Markdown file \ No newline at end of file diff --git a/LinqStudy.Test/LinqBase/初始化器.md b/LinqStudy.Test/LinqBase/初始化器.md new file mode 100644 index 0000000..b124f4b --- /dev/null +++ b/LinqStudy.Test/LinqBase/初始化器.md @@ -0,0 +1 @@ +# Markdown file \ No newline at end of file diff --git a/LinqStudy.Test/LinqBase/匿名类与匿名方法.md b/LinqStudy.Test/LinqBase/匿名类与匿名方法.md new file mode 100644 index 0000000..b124f4b --- /dev/null +++ b/LinqStudy.Test/LinqBase/匿名类与匿名方法.md @@ -0,0 +1 @@ +# Markdown file \ No newline at end of file diff --git a/LinqStudy.Test/LinqToObject/BaseTest.cs b/LinqStudy.Test/LinqToObject/BaseTest.cs new file mode 100644 index 0000000..18b8b3d --- /dev/null +++ b/LinqStudy.Test/LinqToObject/BaseTest.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; + +using Xunit; + +namespace LinqStudy.Test.LinqToObject +{ + /// <summary> + /// 基本项测试 + /// </summary> + public class BaseTest + { + /// <summary> + /// 数据源为 Null,引发异常. + /// </summary> + /// <exception cref="ArgumentNullException"> + /// ArgumentNullException + /// </exception> + /// <remarks> + /// 对Linq操作符而言,数据源为Null时,均引发异常。 + /// </remarks> + [Fact] + public void DataSource_IsNull_Test() + { + List<Person> person = null; + + Assert.ThrowsAny<ArgumentNullException>(() => + { + //引发异常 + var query = person.Where(p => p == null); + }); + } + + /// <summary> + /// 数据源数据项为0时,不会引发异常. + /// </summary> + [Fact] + public void DataSource_Item_0_Test() + { + List<Person> person = new List<Person>(); + + //数据源为没有任何内容项时,即 Count=0,不会引发异常。 + Action action = () => + { + //查不到任何数据,不返回null,而是返回 Count=0的对象。 + person.Where(p => p == null).ToList(); + }; + + //不引发异常 + action.Should().NotThrow(); + } + + /// <summary> + /// 数据源数据项为0时,查询等操作,不返回null,而返回“0数据项的”IEnumerable<T>对象。 + /// </summary> + [Fact] + public void DataSource_Item0_Return_Test() + { + List<Person> person = new List<Person>(); + + //数据源为没有任何内容项时,即 Count=0,不会引发异常。 + Action action = () => + { + //查不到任何数据,不返回null,而是返回 Count=0的对象。 + person.Where(p => p == null).ToList(); + }; + + //不引发异常 + action.Should().NotThrow(); + } + + /// <summary> + /// 查询不到数据项时,不返回null,而是返回“0数据项的”IEnumerable<T>对象 + /// </summary> + [Fact] + public void NoQueryItem_Return_Test() + { + List<Person> person = new List<Person>() + { + new Person(){ Id=1,Name="小屁孩",Age=87}, + new Person(){ Id=2,Name="小屁孩",Age=45}, + new Person(){ Id=3,Name="小屁孩",Age=55}, + new Person(){ Id=4,Name="小屁孩",Age=23}, + }; + + //查不到数据时,返回Count=0的IEnumerable<T>对象 + var result = person.Where(p => p.Age < 10).ToList(); + + Assert.Empty(result); + } + } +} diff --git a/LinqStudy.Test/LinqToObject/CreateTest.cs b/LinqStudy.Test/LinqToObject/CreateTest.cs new file mode 100644 index 0000000..0aaacc5 --- /dev/null +++ b/LinqStudy.Test/LinqToObject/CreateTest.cs @@ -0,0 +1,11 @@ +using System; + +namespace LinqStudy.Test.LinqToObject +{ + /// <summary> + /// 生成操作符 + /// </summary> + public class CreateTest + { + } +} diff --git a/LinqStudy.Test/LinqToObject/OtherTest.cs b/LinqStudy.Test/LinqToObject/OtherTest.cs new file mode 100644 index 0000000..b7285b1 --- /dev/null +++ b/LinqStudy.Test/LinqToObject/OtherTest.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +namespace LinqStudy.Test.LinqToObject +{ + public class OtherTest + { + /// <summary> + /// is操作符用作if条件时,判断之后可以直接赋值给一个变量.(输出给变量) + /// 其它地方可以用as实现相同功能 + /// </summary> + /// <example> + /// if (id is List-int output){output.Add(1);} + /// </example> + [Fact] + public void Is_Test() + { + List<int> id = new List<int>(); + + if (id is List<int> output) + { + output.Add(1); + } + + var cc = id as List<int>; + cc.Add(1); + } + } +} diff --git a/LinqStudy.Test/LinqToObject/ProjectiveTest.cs b/LinqStudy.Test/LinqToObject/ProjectiveTest.cs new file mode 100644 index 0000000..0386b74 --- /dev/null +++ b/LinqStudy.Test/LinqToObject/ProjectiveTest.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Xunit; + +using LinqStudy; + +namespace LinqStudy.Test.LinqToObject +{ + /// <summary> + /// 投影操作符 + /// 投影:遍历序列中的元素,将元素由一种类型转换为另一种类型的操作,并返回由转换后元素组成的新序列。 + /// 即是:IEnumerable<TSource> => IEnumerable<TResult> + /// </summary> + public class ProjectiveTest + { + /// <summary> + /// Select投影:简单投影,一对一 + /// </summary> + [Fact] + public void Select_Test() + { + // Arrange + var persons = PersonManager.GetPersons(); + + // Act + var maps = persons.Select(p=>p.Age).ToList(); + + // Assert + Assert.IsType<List<int>>(maps); + } + /// <summary> + /// 投影为匿名类 + /// </summary> + [Fact] + public void Select_Anonymous_Test() + { + // Arrange + var persons = PersonManager.GetPersons(); + + // Act + var maps = persons.Select(p=>new {Id=p.Id, Node=$"姓名{p.Name},年龄{p.Age}."}).ToList(); + + // Assert + Assert.IsNotType<List<Person>>(maps); + } + + /// <summary> + /// 投影传入索引(序号)参数 + /// </summary> + [Fact] + public void Select_Index_Test() + { + var persons = PersonManager.GetPersons(); + + var maps = persons.Select((query,index)=> new KeyValuePair<int, Person>(index,query)).ToList(); + var indexs = persons.Select((query, index) => index).ToList(); + + Assert.IsType<List<KeyValuePair<int, Person>>>(maps); + Assert.Equal(0,indexs[0]); + } + + /// <summary> + /// SelectMany:复合投影,一对多,合并多到一个集合。 + /// 枚举源序列,将源序列每一项投影为新的集合,合并所有新集合为一个可枚举序列,做为返回值; + /// 提供了将多个 from子句组合起来的功能,它将每个对象的结果合并成单个可枚举序列。 + /// </summary> + [Fact] + public void SelectMany_Test() + { + var employees = new List<Employee>() + { + new Employee(){Id=1,Name="小明",Emails=new List<string>(){ "abc@163.com", "acd@163.com", "ade@163.com" } }, + new Employee(){Id=2,Name="大壮",Emails=new List<string>(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } }, + new Employee(){Id=3,Name="周羊",Emails=new List<string>(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } }, + new Employee(){Id=4,Name="承承",Emails=new List<string>(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } }, + new Employee(){Id=5,Name="东升",Emails=new List<string>(){ "ebc@163.com", "ecd@163.com", "ede@163.com" }}, + }; + + var maps = employees.SelectMany(q=>q.Emails).ToList(); + + Assert.IsType<List<string>>(maps); + Assert.Equal(15, maps.Count); + } + + /// <summary> + /// 投影传入索引(序号)参数 + /// </summary> + [Fact] + public void SelectMany_Index_Test() + { + var employees = new List<Employee>() + { + new Employee(){Id=1,Name="小明",Emails=new List<string>(){ "abc@163.com", "acd@163.com", "ade@163.com" } }, + new Employee(){Id=2,Name="大壮",Emails=new List<string>(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } }, + new Employee(){Id=3,Name="周羊",Emails=new List<string>(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } }, + new Employee(){Id=4,Name="承承",Emails=new List<string>(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } }, + new Employee(){Id=5,Name="东升",Emails=new List<string>(){ "ebc@163.com", "ecd@163.com", "ede@163.com" }}, + }; + + var maps = employees.SelectMany((q, idx) => + { + q.Emails.Add(idx.ToString()); + return q.Emails; + }).ToList(); + + Assert.IsType<List<string>>(maps); + Assert.Equal(20, maps.Count); + } + + /// <summary> + /// 自定义结果项:源项与源项选择的集合项,一一组合,组成新自定义序列。 + /// 类似Cross JOIN + /// </summary> + [Fact] + public void SelectMany_TCollection_Test() + { + var employees = new List<Employee>() + { + new Employee(){ Id=1,Name="小明",Emails=new List<string>(){ "a1@163.com", "a2@163.com", "a3@163.com" } }, + new Employee(){ Id=2,Name="大壮",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } }, + new Employee(){ Id=3,Name="周羊",Emails=new List<string>(){ "c1@163.com", "c2@163.com", "c3@163.com" } }, + new Employee(){ Id=4,Name="承承",Emails=new List<string>(){ "d1@163.com", "d2@163.com", "d3@163.com" } }, + new Employee(){ Id=5,Name="东升",Emails=new List<string>(){ "e1@163.com", "e2@163.com", "e3@163.com" } }, + }; + + var maps = employees.SelectMany((employee)=> employee.Emails,(person,email)=> new { Name=person.Name,Email=email}).ToList(); + + Assert.Equal(15, maps.Count); + } + + /// <summary> + /// 自定义结果项:带索引 + /// 类似Cross JOIN + /// </summary> + [Fact] + public void SelectMany_TCollection_Index_Test() + { + var employees = new List<Employee>() + { + new Employee(){ Id=1,Name="小明",Emails=new List<string>(){ "a1@163.com", "a2@163.com", "a3@163.com" } }, + new Employee(){ Id=2,Name="大壮",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } }, + new Employee(){ Id=3,Name="周羊",Emails=new List<string>(){ "c1@163.com", "c2@163.com", "c3@163.com" } }, + new Employee(){ Id=4,Name="承承",Emails=new List<string>(){ "d1@163.com", "d2@163.com", "d3@163.com" } }, + new Employee(){ Id=5,Name="东升",Emails=new List<string>(){ "e1@163.com", "e2@163.com", "e3@163.com" } }, + }; + + var maps = employees.SelectMany + ( + (employee, idx) => + { + return new List<Employee>() { new Employee() { Id = employee.Id, Name = employee.Name + "_" + idx, Emails = employee.Emails } }; + }, + + (person,email) => new + { + Name = email.Name, + Email = email.Emails + } + ) + .ToList(); + + Assert.Equal(5, maps.Count); + } + } +} diff --git a/LinqStudy/Models/Employee.cs b/LinqStudy/Models/Employee.cs new file mode 100644 index 0000000..1db3c25 --- /dev/null +++ b/LinqStudy/Models/Employee.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LinqStudy +{ + /// <summary> + /// 雇员 + /// </summary> + public class Employee + { + public int Id { get; set; } + public string Name { get; set; } + public List<string> Emails { get; set; } + } +}