LINQ to Objects

通过LINQ to Objects 可以使用 LINQ 直接查询

任何 IEnumerable<T> 或 IQueryable<T> 集合(例如string[]、 int[] 和 List<T>),

而不需要使用直接的 LINQ 提供程序或 API。

 

 一、LINQ to Objects

 

假设有一个类型为 string 的数组,该数组包含姓名列表。

下面的程序输出该字符串数组中以字符G开头的所有姓名:

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

namespace Demo_LINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] allNames = new string[]{
                "Jeffrey", "Kirby", "Gabriel", "Philip", "Ross", "Adam",
                "Alston", "Warren", "Garfield"};

            foreach (string str in allNames)
            {
                if (str.StartsWith("G"))
                    Console.WriteLine(str);
            }

            Console.ReadKey();
        }
    }
}

 

使用 LINQ to Objects,可以按照如下代码所示重写该程序:

namespace Demo_LINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] allNames = new string[]{
                "Jeffrey", "Kirby", "Gabriel", "Philip", "Ross", "Adam",
                "Alston", "Warren", "Garfield"};

            IEnumerable<string> foundNames =
                from name in allNames
                where name.StartsWith("G")
                select name;
            foreach (string str in foundNames)
                Console.WriteLine(str);

            Console.ReadKey();
        }
    }
}

  

IEnumerable<string> foundNames =
                from name in allNames
                where name.StartsWith("G")
                select name;

该示例中查询结果是:IEnumerable<string>,当然也可一使用隐式类型化功能让编译器自动推断类型,如:

var foundNames =
                from name in allNames
                where name.StartsWith("G")
                select name;

 在使用更为复杂的筛选时,LINQ 的使用性就更加明显,如:

var foundNames =
       from name in allNames
       where name.StartsWith("G") && name.EndsWith("d")
       select name;


下面是以整数数组,用 LINQ 检索出奇数,并按降序排列:

int[] nums = { 12, 34, 10, 3, 45, 6, 87, 49, 23, 32 };

var oddNums = from n in nums
              where (n % 2 == 1)      // 检索奇数
              orderby n descending    // 降序排列
              select n;
foreach (int n in oddNums)
      Console.WriteLine(n);

然后可以使用 以下方法检索出奇数的个数:       

int oddCount = oddNums.Count();

也可以将结果转换为 int 数组:      

int[] oddNumsArray = oddNums.ToArray();

 

二、查询语法与查询方法的对比以及拉姆达表达式

 

上面使用的 LINQ 查询 都使用了查询语法,如下:

var oddNums = from n in nums
              here (n % 2 == 1)      // 检索奇数
              orderby n descending   // 降序排列
              select n;

除了使用查询语法之外,也可以使用方法语法:

var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);

(n => n % 2 == 1)

称为拉姆达表达式,=> 是拉姆达运算符,读作“ goes to(转到)”,

可以将这个拉姆达表达式认为是如下函数:

static bool function(int n)
{
         return ( n %2 == 1);
}

 

可以使用方法语法重写前面的字符串数组示例,如下:

var foundNames = allNames.Where(
       name => name.StartWith("G") && name.EndWith("l"));

  

在这两种语法中,应该如何选择使用何种语法?

      <1> 方法语法和查询语法之间没有性能上的区别。

      <2> 查询语法更容易阅读,因此尽可能使用这种语法。

      <3> 在没有相等的查询语法的情况下使用方法语法。

              例如, Count 和 Max 方法就没有相等的查询语法。

 

三、延迟查询执行 与 强制立即执行查询

 

1、延迟查询执行

 

var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);

  该语句中 oddNum s变量并没有存储满足条件的查询结果,该变量只存储了查询(可以理解为有了方法,到位执行,当然没有结果了);

只有在遍历查询变量时,才会执行该查询。如执行以下语句时,才执行查询,返回结果:

foreach (int n in oddNums)
                Console.WriteLine(n);

这种概念成为延迟查询。意义在于:仅建立一个查询,每次执行该查询时都会获得最新的结果!

 

为了证明延迟查询,下面程序首先定义一个查询,然后用 foreach 遍历输出结果,

将数组中的每个元素都加20,然后再次致谢 foreach 循环。

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

            int[] nums = { 12, 34, 10, 3, 45, 6, 87, 49, 23, 32 };
            var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);

            Console.WriteLine("First execution");
            foreach (int n in oddNums)
                Console.WriteLine(n);

            for (int i = 0; i < 10; i++)
                nums[i] += 20;

            Console.WriteLine("\nSecond execution");
            foreach (int n in oddNums)
                Console.WriteLine(n);

            Console.ReadKey();
        }
    }
}

返回的结果为:

First execution

87

49

45

23

3

 

Second execution

107

69

65

43

23

两次结果不同,证明了查询直到被访问时才被执行!

 

2、强制立即执行查询

    强制立即执行查询的一种方法是将查询结果显示的转换为 List 对象即可!

var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);

    把该语句修改为:

var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n).ToList();

你会发现两次 foreach 循环的结果是相同的!

 

 四、LINQ 和匿名类型

 

先看下面的代码:

namespace Demo_LINQ
{
    // 定义 Contact 类
    public class Contact
    {
        public int id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 假设有包含 Contact 对象的列表,如下所示:
            List<Contact> Contacts = new List<Contact>()
            {
                new Contact() {id=1, FirstName = "Jhon",    LastName = "Chen"},
                new Contact() {id=2, FirstName = "Maryann", LastName = "Chen"},
                new Contact() {id=3, FirstName = "Richard", LastName = "Wells"}
            };

            // 可以用 LINQ 查询姓为 Chen 的所有联系人:
            IEnumerable<Contact> foundContacts =
                from c in Contacts
                where c.LastName == "Chen"
                select c;

            // foundContacts 对象的类型是 IEnumerable<Contact>
            // 可以用 foreach 循环输出所有姓 “Chen”的联系人
            foreach (var c in foundContacts)
                Console.WriteLine("{0} - {1} {2}",
                    c.id, c.FirstName, c.LastName);

            Console.ReadKey();
        }
    }
}

 

然而,可以修改查询以使结果形成自定义类而非 Contac 类型。如下:

var foundContacts =
                from c in Contacts
                where c.LastName == "Chen"
                select new
                {
                    id = c.id,
                    Name = c.FirstName + " " + c.LastName
                };

 foreach (var c in foundContacts)
      Console.WriteLine("{0} - {1}", c.id, c.Name);

值得注意的是:必须使用关键字 var 让编译器自动推断 foundContacts 的类型;

                    因为结果是当前定义的匿名类型。

posted on 2012-07-09 17:17  huobaby  阅读(245)  评论(0编辑  收藏  举报