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; }
+    }
+}