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`接口。 +> +> 因为IQueryable和`IQueryable`也实现了IEnumerable或`IEnumerable接口`,所以也可以使用Linq To Object。 +> 泛型集合均实现了`IEnumerable`接口,可以直接使用。 +> 一些集合类,也提供了转换扩展方法,可以转换后使用。 +> + +## Linq To Object本质 + +> 本质是扩展方法,没有相应的 Linq Provider,这点与 Linq To SQL不同。 + +## 注意事项 + ++ 延时执行 + + > 写好操作步骤时,只是进行了“定义”,直到调用(转换方法、聚合方法会立即执行)时才会真正执行操作。这点特别注意。 + ++ 异常 + + > 当对象为null时,通常都会抛出“参数null值”异常。使用前,就确保对象不为null + + ``` csharp + [Fact] + public void Null_Test() + { + List person = null; + + //对Linq操作符而言,基本上数据源为Null时,将引发异常。 + Assert.ThrowsAny(() => + { + //此处引发异常 + 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 +{ + /// + /// 基本项测试 + /// + public class BaseTest + { + /// + /// 数据源为 Null,引发异常. + /// + /// + /// ArgumentNullException + /// + /// + /// 对Linq操作符而言,数据源为Null时,均引发异常。 + /// + [Fact] + public void DataSource_IsNull_Test() + { + List person = null; + + Assert.ThrowsAny(() => + { + //引发异常 + var query = person.Where(p => p == null); + }); + } + + /// + /// 数据源数据项为0时,不会引发异常. + /// + [Fact] + public void DataSource_Item_0_Test() + { + List person = new List(); + + //数据源为没有任何内容项时,即 Count=0,不会引发异常。 + Action action = () => + { + //查不到任何数据,不返回null,而是返回 Count=0的对象。 + person.Where(p => p == null).ToList(); + }; + + //不引发异常 + action.Should().NotThrow(); + } + + /// + /// 数据源数据项为0时,查询等操作,不返回null,而返回“0数据项的”IEnumerable对象。 + /// + [Fact] + public void DataSource_Item0_Return_Test() + { + List person = new List(); + + //数据源为没有任何内容项时,即 Count=0,不会引发异常。 + Action action = () => + { + //查不到任何数据,不返回null,而是返回 Count=0的对象。 + person.Where(p => p == null).ToList(); + }; + + //不引发异常 + action.Should().NotThrow(); + } + + /// + /// 查询不到数据项时,不返回null,而是返回“0数据项的”IEnumerable对象 + /// + [Fact] + public void NoQueryItem_Return_Test() + { + List person = new List() + { + 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对象 + 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 +{ + /// + /// 生成操作符 + /// + 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 + { + /// + /// is操作符用作if条件时,判断之后可以直接赋值给一个变量.(输出给变量) + /// 其它地方可以用as实现相同功能 + /// + /// + /// if (id is List-int output){output.Add(1);} + /// + [Fact] + public void Is_Test() + { + List id = new List(); + + if (id is List output) + { + output.Add(1); + } + + var cc = id as List; + 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 +{ + /// + /// 投影操作符 + /// 投影:遍历序列中的元素,将元素由一种类型转换为另一种类型的操作,并返回由转换后元素组成的新序列。 + /// 即是:IEnumerable => IEnumerable + /// + public class ProjectiveTest + { + /// + /// Select投影:简单投影,一对一 + /// + [Fact] + public void Select_Test() + { + // Arrange + var persons = PersonManager.GetPersons(); + + // Act + var maps = persons.Select(p=>p.Age).ToList(); + + // Assert + Assert.IsType>(maps); + } + /// + /// 投影为匿名类 + /// + [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>(maps); + } + + /// + /// 投影传入索引(序号)参数 + /// + [Fact] + public void Select_Index_Test() + { + var persons = PersonManager.GetPersons(); + + var maps = persons.Select((query,index)=> new KeyValuePair(index,query)).ToList(); + var indexs = persons.Select((query, index) => index).ToList(); + + Assert.IsType>>(maps); + Assert.Equal(0,indexs[0]); + } + + /// + /// SelectMany:复合投影,一对多,合并多到一个集合。 + /// 枚举源序列,将源序列每一项投影为新的集合,合并所有新集合为一个可枚举序列,做为返回值; + /// 提供了将多个 from子句组合起来的功能,它将每个对象的结果合并成单个可枚举序列。 + /// + [Fact] + public void SelectMany_Test() + { + var employees = new List() + { + new Employee(){Id=1,Name="小明",Emails=new List(){ "abc@163.com", "acd@163.com", "ade@163.com" } }, + new Employee(){Id=2,Name="大壮",Emails=new List(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } }, + new Employee(){Id=3,Name="周羊",Emails=new List(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } }, + new Employee(){Id=4,Name="承承",Emails=new List(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } }, + new Employee(){Id=5,Name="东升",Emails=new List(){ "ebc@163.com", "ecd@163.com", "ede@163.com" }}, + }; + + var maps = employees.SelectMany(q=>q.Emails).ToList(); + + Assert.IsType>(maps); + Assert.Equal(15, maps.Count); + } + + /// + /// 投影传入索引(序号)参数 + /// + [Fact] + public void SelectMany_Index_Test() + { + var employees = new List() + { + new Employee(){Id=1,Name="小明",Emails=new List(){ "abc@163.com", "acd@163.com", "ade@163.com" } }, + new Employee(){Id=2,Name="大壮",Emails=new List(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } }, + new Employee(){Id=3,Name="周羊",Emails=new List(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } }, + new Employee(){Id=4,Name="承承",Emails=new List(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } }, + new Employee(){Id=5,Name="东升",Emails=new List(){ "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>(maps); + Assert.Equal(20, maps.Count); + } + + /// + /// 自定义结果项:源项与源项选择的集合项,一一组合,组成新自定义序列。 + /// 类似Cross JOIN + /// + [Fact] + public void SelectMany_TCollection_Test() + { + var employees = new List() + { + new Employee(){ Id=1,Name="小明",Emails=new List(){ "a1@163.com", "a2@163.com", "a3@163.com" } }, + new Employee(){ Id=2,Name="大壮",Emails=new List(){ "b1@163.com", "b2@163.com", "b3@163.com" } }, + new Employee(){ Id=3,Name="周羊",Emails=new List(){ "c1@163.com", "c2@163.com", "c3@163.com" } }, + new Employee(){ Id=4,Name="承承",Emails=new List(){ "d1@163.com", "d2@163.com", "d3@163.com" } }, + new Employee(){ Id=5,Name="东升",Emails=new List(){ "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); + } + + /// + /// 自定义结果项:带索引 + /// 类似Cross JOIN + /// + [Fact] + public void SelectMany_TCollection_Index_Test() + { + var employees = new List() + { + new Employee(){ Id=1,Name="小明",Emails=new List(){ "a1@163.com", "a2@163.com", "a3@163.com" } }, + new Employee(){ Id=2,Name="大壮",Emails=new List(){ "b1@163.com", "b2@163.com", "b3@163.com" } }, + new Employee(){ Id=3,Name="周羊",Emails=new List(){ "c1@163.com", "c2@163.com", "c3@163.com" } }, + new Employee(){ Id=4,Name="承承",Emails=new List(){ "d1@163.com", "d2@163.com", "d3@163.com" } }, + new Employee(){ Id=5,Name="东升",Emails=new List(){ "e1@163.com", "e2@163.com", "e3@163.com" } }, + }; + + var maps = employees.SelectMany + ( + (employee, idx) => + { + return new List() { 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 +{ + /// + /// 雇员 + /// + public class Employee + { + public int Id { get; set; } + public string Name { get; set; } + public List Emails { get; set; } + } +}