C#沉淀-Linq的使用

Linq 可以轻松的查询对象集合。Linq代表语言集成查询,是.NET框架的扩展,支持从数据库、程序对象的集合以及XML文档中查询数据

一个简单的示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个int数组,作为被查询的数据源
            int[] numbers = { 1, 2, 3, 4, 5, 18 };

            //Linq定义查询,注意,这里只是“定义”而已
            IEnumerable<int> lowNum =
                from nu in numbers
                where nu < 10
                select nu;

            //遍历lowNum,只有使用lowNum的时候,数据才会被查询出来
            //所以,在这里才被执行了查询
            foreach (int item in lowNum)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

针对于不同的数据源,需要实现相应的Linq查询的代码模块,这些代码模块被称作Linq提供程序。在C#中,觉的Linq提供程序有Linq to Object/ Linq to XML/ BLinq(Asp.Net)

匿名类

在深入了解Linq之前,需要先了解一下匿名类,因为在使用Linq语句的时候,会大量的使用匿名类

示例:使用匿名类型创建一个学生类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个学生类
            var student = new { Name = "Jack", Age = 18, Class = "013" };

            Console.ReadKey();
        }
    }
}

解析:

创建一个匿名类需要用到关键字new,然后直接在后面跟{ }来初始化类中成员(属性),多个成员之间使用逗号分隔;因为没有指定类型,所有在接收创建的对象时,需要用到关键字var,而且必须使用var关键字

  • 匿名类型只能和局部变量配合使用,不能用于类成员
  • 由于匿名类没有名字,所以必须以var关键字作为变量类型
  • 不能设置匿名类型对象的属性,因为匿名类的成员是只读的

在初始化一个匿名类型对象时,其成员的初始化不仅可以使用赋值操作,还可以使用成员访问表达式标识符形式

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Other
    {
        public static string Name = "Bob";
    }
    class Program
    {
        static void Main(string[] args)
        {
            //局部变量,表示班级
            string Class = "013";

            //创建一个学生类
            var student = new { Other.Name, Age = 18, Class };

            //访问学生类中成员
            Console.WriteLine("My name is "+student.Name);
            Console.WriteLine("I'm "+student.Age+" years old");
            Console.WriteLine("My Class is " + student.Class);

            Console.ReadKey();
        }
    }
}

var student = new { Other.Name, Age = 18, Class };的效果等同于var student = new { Name = Other.Name, Age = 18, Class = Class};

如果再声明一个具有相同的参数名、相同的推断类型和相同顺序的匿名类型的话,编译器会重用这个类型直接创建新的实例,而不会创建新的匿名类型

方法语法和查询语法

查询语法:看上去和SQL语句很相似,使用查询表达形式书写

方法语法:使用标准的方法调用

查询语法是声明式的,但未指明如何执行这个查询;方法语法是命令式的,它指明了方法查询调用的顺序

编译器会将使用语法表示的查询翻译为方法调用的形式,在运行时这两种方式没有性能上的差异

先看方法语法与查询语法的示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Other
    {
        public static string Name = "Bob";
    }
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个int数组,作为被查询的数据源
            int[] numbers = { 1, 2, 3, 4, 5, 18 };

            //查询语法
            var _numbers =
                from nu in numbers
                where nu < 10
                select nu;

            //方法语法,Where方法的参数使用了Lambda表达式
            var _num = numbers.Where(x => x < 10);


            foreach (int item in _numbers)
            {
                Console.WriteLine(item);
            }

            foreach (int item in _num)
            {
                Console.WriteLine(item);
            }
            
            Console.ReadKey();
        }
    }
}

查询变量

Linq查询返回的结果可以是一个枚举,也可以是一个叫做标量的单一值

示例:

//创建一个int数组,作为被查询的数据源
int[] numbers = { 1, 2, 3, 4, 5, 18 };

//返回一个IEnumerable结果,它可以枚举返回的结果
IEnumerable<int> _numbers =
    from nu in numbers
    where nu < 10
    select nu;

//通过Count()方法返回查询结果总数量
int _count =
    (from nu in numbers
     where nu < 10
     select nu).Count();

等号左边的变量叫做查询变量,这里指_numbers_count

查询变量一般使用var类型来让编译器自动推断其返回的类型

如果查询语句返回的是枚举类型,查询变量中是不会包含结果的,只有在真正使用枚举值的时候才会执行查询,并且每次使用枚举值的时候都会执行一次查询语句;而如果查询语句返回的是标题,查询则立即生效,并把结果保存在查询变量中

查询表达式的结构

from子名指定数据源,并且引入迭代变量;迭代变量逐个表示数据源的每一个元素;语法如下:

from [Type] item in ItemsItems表示数据源;item表示数据源中的元素;Type是可选的,表示元素的类型

join子句,联结语句可以结合两个或多个集合中的数据,然后产生一个临时的对象集合,每个集合中都包含原始集合对象中的所有元素,语法如下:

join Identifier in Collection2 on Field1 equqls Field2

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Course//课程类
    {
        public int ID;
        public int Student_ID;
        public string Course_Name;
    }
    class Student//学生类
    {
        public int ID;
        public string Name;
    }
    class Program
    {
        static void Main(string[] args)
        {
            //学生类集合
            Student[] st = new Student[] {
                new Student{ID=111,Name="Bob"},
                new Student{ID=112,Name="Jack"},
                new Student{ID=113,Name="Hong"}
            };

            //课程类集合
            Course[] co = new Course[] {
                new Course{ID=1, Student_ID=111,Course_Name="数学"},
                new Course{ID=2, Student_ID=112,Course_Name="语文"},
                new Course{ID=3, Student_ID=113,Course_Name="化学"},
                new Course{ID=4, Student_ID=112,Course_Name="数学"},
                new Course{ID=5, Student_ID=112,Course_Name="生物"}
            };

            //Linq查询语法
            var result =
                from a in st //指定第一个数据源st
                join b in co on a.ID equals b.Student_ID //联结第二个数据源ot,并用on指定联结条件,equals来指定比较字段
                where b.Course_Name=="数学" //匹配数学课程
                select a.Name; //返回名字

            foreach (var name in result)
            {
                Console.WriteLine("参加数学课程的学生名:"+name);
            }
            
            Console.ReadKey();
        }
    }
}

from...let...where片段

可以使用多个from子句指定多个数据源,示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Course//课程类
    {
        public int ID;
        public int Student_ID;
        public string Course_Name;
    }
    class Student//学生类
    {
        public int ID;
        public string Name;
    }
    class Program
    {
        static void Main(string[] args)
        {
            //学生类集合
            Student[] st = new Student[] {
                new Student{ID=111,Name="Bob"},
                new Student{ID=112,Name="Jack"},
                new Student{ID=113,Name="Hong"}
            };

            //课程类集合
            Course[] co = new Course[] {
                new Course{ID=1, Student_ID=111,Course_Name="数学"},
                new Course{ID=2, Student_ID=112,Course_Name="语文"},
                new Course{ID=3, Student_ID=113,Course_Name="化学"},
                new Course{ID=4, Student_ID=112,Course_Name="数学"},
                new Course{ID=5, Student_ID=112,Course_Name="生物"}
            };

            //指定多个数据源
            var st_co =
                from a in st
                from b in co
                where a.ID == b.Student_ID && b.Course_Name=="数学"
                select new { a.Name, b.Course_Name };//创建一个匿名类型对象

            //访问返回集体中的成员
            foreach (var item in st_co)
            {
                Console.WriteLine("学生:"+item.Name);
                Console.WriteLine("课程:"+item.Course_Name);
            }
            
            Console.ReadKey();
        }
    }
}

let子句接受一个表达式的运算,并且把它赋值给一个需要在其它地方运算中使用的标识符

示例:

//定义两个数据源
int[] number1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 1, 2, 3, 4, 5, 18 };

var nu_array =
    from a in number1
    from b in numbers2
    let sum = a + b //使用let子句将第一个集合中的元素与第二个集合中的元素进行相加
    where sum == 4
    select new { a, b, sum };

foreach (var item in nu_array)
{
    Console.WriteLine(item.a + "," + item.b + "," + item.sum);
}

where子句根据之后运算来去除不符合指定条件的项,在from...let...where片段中可以有任意多个where子句

示例:

//定义两个数据源
int[] number1 = { 1, 2, 3, 4, 5 };
int[] numbers2 = { 1, 2, 3, 4, 5, 18 };

var nu_array =
    from a in number1
    from b in numbers2
    let sum = a + b //使用let子句将第一个集合中的元素与第二个集合中的元素进行相加
    where sum == 4 //筛选a+b等于4的所有元素
    where a == 2 //再指定a必须等于2,那返回的结果中,b就只能是等于2了
    select new { a, b, sum };

foreach (var item in nu_array)
{
    Console.WriteLine(item.a + "," + item.b + "," + item.sum);
}

orderby子句

orderby子句接受一个表达式,并根据表达式按顺序返回结果;排列的表达式也可以是集合中的成员

  • orderby子句默认是按升序排列的;可以使用ascending显示的指定为升序或使用descending指定为隆序
  • 可以有任意多个子句,之间使用逗号分隔

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Course//课程类
    {
        public int ID;
        public int Student_ID;
        public string Course_Name;
    }
    class Student//学生类
    {
        public int ID;
        public string Name;
        public int Age;
    }
    class Program
    {
        static void Main(string[] args)
        {
            //学生类集合
            Student[] st = new Student[] {
                new Student{ID=111,Name="Bob",Age=12},
                new Student{ID=112,Name="Jack",Age=15},
                new Student{ID=113,Name="Hong",Age=9}
            };

            //课程类集合
            Course[] co = new Course[] {
                new Course{ID=1, Student_ID=111,Course_Name="数学"},
                new Course{ID=2, Student_ID=112,Course_Name="语文"},
                new Course{ID=3, Student_ID=113,Course_Name="化学"},
                new Course{ID=4, Student_ID=112,Course_Name="数学"},
                new Course{ID=5, Student_ID=112,Course_Name="生物"}
            };

            var query = from student in st
                        orderby student.Age // 根据Age字段进行排序
                        select student;

            foreach (var item in query)
            {
                Console.WriteLine(string.Format("ID:{0},名字:{1},年龄:{2}",item.ID,item.Name,item.Age));
            }

            Console.ReadKey();
        }
    }
}

select...group子句

select子句 指定所选对象的哪部分应该被选择;指定的部分可以是整个数据项,或数据项的一个字段,或数据项的几个字段组成的新的对象

group by子句 是可选的,用来指定选择的项如何分组

select子句示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Student//学生类
    {
        public int ID;
        public string Name;
        public int Age;
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            //学生类集合
            Student[] st = new Student[] {
                new Student{ID=111,Name="Bob",Age=12},
                new Student{ID=112,Name="Jack",Age=15},
                new Student{ID=113,Name="Hong",Age=9}
            };
            
            var query = from student in st
                        //select student.Name //选择一个字段
                        //select new {student.Name, student.Age} //选择多个字段组成的新对象
                        select student;// 选择所有的sutdent元素

            foreach (var item in query)
            {
                Console.WriteLine(string.Format("ID:{0},名字:{1},年龄:{2}",item.ID,item.Name,item.Age));
            }

            Console.ReadKey();
        }
    }
}

查询中的匿名类——查询结果可以由原始集合的项、项的某些字段或匿名类型组成,例如select new {student.Name, student.Age}

group子句将select的对象根据一些标准进行分组

  • 如果项包含在查询语句中,它就可以根据某个字段的值进行分组;作为分组的依据的属性叫做健(key)
  • gorup将返回可以枚举已经形成的项的分组的可枚举类型
  • 分组本身是可被枚举的

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Student//学生类
    {
        public int ID;
        public string Name;
        public int Age;
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            //学生类集合
            Student[] st = new Student[] {
                new Student{ID=111,Name="Bob",Age=12},
                new Student{ID=112,Name="Jack",Age=15},
                new Student{ID=113,Name="Json",Age=15},
                new Student{ID=114,Name="Hong",Age=9}
            };
            
            var query = 
                from student in st
                group student by student.Age; //按照年龄来分组                       

            foreach (var item in query)
            {
                Console.WriteLine("年龄组:"+item.Key); //通过Key来找到分组的依据
                foreach (var it in item)
                {
                    Console.WriteLine("\t名字:"+it.Name);
                }
            }

            Console.ReadKey();
        }
    }
}

查询延续:into子句

查询延续子句可以接受查询的一部分结果并赋予一个名字,从而可以在查询的另一部分中使用

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Course//课程类
    {
        public int ID;
        public int Student_ID;
        public string Course_Name;
    }
    class Student//学生类
    {
        public int ID;
        public string Name;
        public int Age;
    }
    class Program
    {
        static void Main(string[] args)
        {
            int[] number1 = { 1, 2, 3, 4, 5 };
            int[] numbers2 = { 1, 2, 3, 4, 5, 18 };

            var result =
                from a in number1
                join b in numbers2 on a equals b
                into a_b //通过into将number1与numbers2联合命名为a_b
                from c in a_b
                select c;

            foreach (var item in result)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

标准查询运算符

  • 被查询的对象叫做序列,它必须实现IEnumberable<T>接口
  • 标准查询运算符使用方法语法
  • 一些运算符返回IEnumberable对象,而其他的一些 运算符返回标量
  • 很多操作都可以一个Lambda表达式做为参数

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Course//课程类
    {
        public int ID;
        public int Student_ID;
        public string Course_Name;
    }
    class Student//学生类
    {
        public int ID;
        public string Name;
        public int Age;
    }
    class Program
    {
        static void Main(string[] args)
        {
            int[] number = { 1, 2, 3, 4, 5 };

            //total与hwoMay都是标量
            //被操作的对象number是一个序列
            //而Sum()与Count()是去处符(方法)
            int total = number.Sum();
            int howMany = number.Count();

            Console.ReadKey();
        }
    }
}

序列是指实现了IEnumberable<T>接口的类,包括List<>/Dictionary<>/Stack<>/Array等待

共有47个标题运算符,下面列举几个常用的运算符:

  1. Where -- 根据给定的条件对序列进行过滤
  2. Select -- 指定要包含一个对象或
  3. Join -- 对两个序列执行内联结
  4. GroupBy -- 分组序列中的元素
  5. Dinstinct -- 去除序列中的重复项
  6. ToList -- 将序列作为List<T>返回
  7. First -- 返回序列中第一个与条件相匹配的元素
  8. FirstOrDefault -- 返回序列中第一个与条件相匹配的元素,如果匹配不到,就返回第一个元素
  9. Last -- 返回序列中最后一个与条件相匹配的元素
  10. LastOrDefault -- 返回序列中最后一个与条件相匹配的元素,如果匹配不到,就返回最后一个元素
  11. Count -- 返回序列中元素的个数
  12. Sum -- 返回序列中值的总和
  13. Min -- 返回序列中值的最小值
  14. Max -- 返回序列中值的最大值
  15. Average -- 返回序列中值的平均值
  16. Contains -- 返回一个布尔值,指明序列中是否包含某个元素

标准查询运算符的签名

System.Linq.Enumberable类声明了标准查询运算符方法,它们都扩展了IEnumberable<T>泛型类的扩展方法

扩展方法是公共静态的,尽管定义在一个类中,但目的是为另一个类(第一个形参)增加功能。该参数前必须有关键字this

  • 由于运算符是泛型方法, 因此每个方法名都具有相关泛型参数(T)
  • 由于运算符是扩展IEnumberable的扩展方法,它们必须满足以下的语法条件
    • 声明为Public和Static
    • 在第一个参数前有this指示器
    • IEnumberable<T>作为第一个参数类型

下面来看CountWhereFirst三个方法

//Count
public static int Count<TSource>(this IEnumerable<TSource> source);
//Where
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
//First
public static TSource First<TSource>(this IEnumerable<TSource> source);
  • 它们都是Public和Static的

  • 它们都是一个泛型方法

  • 形参里的this关键字指出了它们量种扩展方法,this后面的泛型类是被扩展的类,在这里可以看出,它们都扩展了IEnumerable<T>

如果关于扩展方法的概念是不是很清楚,请参考泛型,敝人本篇文章有关于扩展方法的详细讲解

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            //数组是IEnumerable<T>的派生类
            int[] number = new int[] { 1, 2, 3, 4, 5 };

            //方法语法,数组作为参数
            //可以直接通过Enumerable静态类来访问相关方法
            var _count = Enumerable.Count(number);
            var _first = Enumerable.First(number);

            //扩展语法,数组被做为被扩展的对象
            //Enumerable扩展了IEnumerable<T>,所以可以在被扩展的类里调用扩展方法
            var __count = number.Count();
            var __first = number.First();

            Console.ReadKey();
        }
    }
}

查询表达式与标准查询运算符结合使用

每一个查询表达式都会被编译器翻译成标准查询运算符的形式,两者可以结合使用,示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] number = new int[] { 1, 2, 3, 4, 5 };

            int _num =
                (from nu in number
                 where nu < 4
                 select nu).Count();

            Console.ReadKey();
        }
    }
}

将委托作为参数

很多运算符接受泛型委托作为参数

再来看看Count方法,它被重载为两个形式

//第一种:经常使用
public static int Count<TSource>(this IEnumerable<TSource> source);
//第二种,除了指示扩展类外,还接收一个泛型方法作为参数
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

第二种形式的Count方法,如果想要传递一个放开方法作为参数,那就只能使用委托,因此Func<TSource, bool> predicate指的就是一个委托形参(当然,这个委托也可以使用Lambda表达式代替)

示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args) 
        {
            int[] number = new int[] { 1, 2, 3, 4, 5 };
            //Lambda会返回条件为Ture的值,这里会返回奇数,再统计个数
            var countOdd = number.Count(n => n % 2 == 1);

            Console.WriteLine("奇数个数:{0}",countOdd);
            Console.ReadKey();
        }
    }
}

Linq预定义的委托类型

为了实现由编程人员提供代码来指示标准运算会如何执行它的操作,标准运算符支持将委托作为参数来实现该目标

Linq定义了两套泛型委托类型与标准查询运算符一起使用,即FancAction委托,各有17个成员

因此在使用的时候要求:

  • 我们用作实参的委托对象必须是这些类型或这些形式之一
  • TResult代表返回值,并且总是在类型参数列表中的最后一个

示例:

public delegate TResult Func<in T, out TResult>(T arg);

ActionFunc相似,只是都没有返回值

使用委托参数的示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        //返回是否是奇数
        static bool IsOdd(int x)
        {
            return x % 2 == 1;
        }
        static void Main(string[] args)
        {

            int[] number = new int[] { 1, 2, 3, 4, 5 };

            //创建一个Func<T, TR>类型委托
            //T是类型参数
            //TR是返回类型
            Func<int, bool> my_func = new Func<int, bool>(IsOdd);
            var countOdd = number.Count(my_func);

            Console.WriteLine("奇数个数:{0}",countOdd);
            Console.ReadKey();
        }
    }
}

使用Lambda表达式参数的示例

当标准运算符所需要的方法参数只用一次,并且块内代码只有一行,那完成可以使用Lambda表达式来完成

示例 :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {

            int[] number = new int[] { 1, 2, 3, 4, 5 };

            //使用Lambda表达式达到的效果是一样的
            var countOdd = number.Count(x => x % 2 == 1);

            Console.WriteLine("奇数个数:{0}",countOdd);
            Console.ReadKey();
        }
    }
}

也可以使用匿名方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeForLinq
{
    class Program
    {
        static void Main(string[] args)
        {

            int[] number = new int[] { 1, 2, 3, 4, 5 };

            //使用匿名方法
            Func<int, bool> my_func = delegate(int x){return x % 2 == 1;};
            var countOdd = number.Count(my_func);

            Console.WriteLine("奇数个数:{0}",countOdd);
            Console.ReadKey();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 应用程序还需要操作存储在其他数据源(如SQL数据库或XML文件)中的数据,甚至通过Web服务访问它们。传统上,查询...
    CarlDonitz阅读 559评论 0 0
  • 19.1 什么是LINQ 在关系型数据库系统中,可以通过 SQL 访问数据库中的任何数据。但在程序中,由于保存在类...
    向着远方奔跑阅读 334评论 0 0
  • 犯邪淫的人杀自己的孩子。我们很多人可能都在想,怎么会杀自己的孩子?两种原因。 第一种原因,男的犯邪淫时间久了,打胎...
    秦东魁阅读 2,915评论 0 0
  • (中华新韵,首句仄起入韵) 黑本(20180518) 打卡督察廿一天,养成习惯露新颜。 生活随意来将就,标准用心去...
    黑本儿阅读 275评论 0 0