可以查询的内容:不是所有的对象都可以使用LINQ to Objects查询,使用LINQ to Objects的首要条件是:需要查询的对象是实现了IEnumerable<T>接口的集合类型;说明:本篇中的例子兼顾使用查询表达式和查询操作符
1. 数组
包括元素类型为Object在内的所有类型的数组都可以使用LINQ to Objects,示例如下:
using System;
using System.Linq;
class ArrayTest
{
class Cutomer
{
public Cutomer(String name, String company, UInt16 age)
{
Name = name;
Company = company;
Age = age;
}
public string Name { get; set; }
public string Company { get; set; }
private ushort age;
public ushort Age
{
get { return age; }
set
{
if (value > 200 || value < 1)
throw new ArgumentOutOfRangeException("age", "Age must between 1 and 200!");
age = value;
}
}
}
static void Main()
{
Object[] array = { 's', "String", true, 28, 26.7, 27.6F, 18UL, new Cutomer("Steven Xue", "Google", 25) };
var types =
array
.Select(item => item.GetType().Name)
.OrderBy(type => type);
ObjectDumper.Write(types);
}
}
输出如下:
Boolean
Char
Cutomer
Double
Int32
Single
String
UInt64
以下示例是强类型的数组:
using System;
using System.Collections.Generic;
using System.Linq;
class TypedArrayTest
{
class Book
{
public String Isbn { get; set; }
public Int32 PageCount { get; set; }
public Decimal Price { get; set; }
public String Publisher { get; set; }
public String Title { get; set; }
public String[] Authors { get; set; }
public override String ToString()
{
return Title;
}
}
static void Main()
{
Book[] books = {
new Book { Title="LINQ in Action" },
new Book { Title="JavaScript in Action" },
new Book { Title="CLR Via C#" }
};
var titles =
books
.Where(book => book.Title.Contains("LINQ"))
.Select(book => book.Title);
ObjectDumper.Write(titles);
}
}
输出如下:
LINQ in Action
说明:ObjectDumper是一个非常有用的辅助类,能够显示对象中的数据,随微软的LINQ示例代码提供(具体位置:VS安装目录\Samples \1033目录下的CSharpSamples.zip中提供了ObjectDumper辅助类);从上面的例子可以看出,查询能够应用在元素为自定义类 型的数组上,以上示例为Cutomer和Book类
2. 泛型列表
以下列出主要的泛型列表:
System.Collections.Generic.List<T>
System.Collections.Generic.LinkedList<T>
System.Collections.Generic.Queue<T>
System.Collections.Generic.Stack<T>
System.Collections.Generic.HashSet<T>
System.Collections.ObjectModel.Collection<T>
System.ComponentModel.BindingList<T>
示例如下:
using System;
using System.Collections.Generic;
using System.Linq;
class GenericListTest
{
class Book
{
public String Isbn { get; set; }
public Int32 PageCount { get; set; }
public Decimal Price { get; set; }
public String Publisher { get; set; }
public String Title { get; set; }
public String[] Authors { get; set; }
public override String ToString()
{
return Title;
}
}
static void Main()
{
List<Book> books = new List<Book>() {
new Book { Title="LINQ in Action" },
new Book { Title="JavaScript in Action" },
new Book { Title="CLR Via C#" }
};
var titles =
books
.Where(book => book.Title.Contains("LINQ"))
.Select(book => book.Title);
ObjectDumper.Write(titles);
}
}
输出如下:
LINQ in Action
注意:查询语句与强类型数组的示例一模一样,没有作任何的改变,因为无论是数组还是列表都实现了同样的IEnumerable<Book>接口
3. 泛型字典
泛型字典实现了IEnumerable<KeyValuePair<TKey, TValue>>接口,KeyValuePair结构包含了强类型的Key和Value属性,以下列出主要的泛型字典:
System.Collections.Generic.Dictionary<TKey, TValue>
System.Collections.Generic.SortedDictionary<TKey, TValue>
System.Collections.Generic.SortedList<TKey, TValue>
示例如下:
class GenericDictionaryTest
{
static void Main()
{
Dictionary<int, string> numbers;
numbers = new Dictionary<int, string>();
numbers.Add(0, "America");
numbers.Add(1, "China");
numbers.Add(2, "England");
numbers.Add(3, "Australia");
numbers.Add(4, "French");
numbers.Add(5, "Japan");
//取键值为偶数的字典元素
var evenNumbers =
from number in numbers
where (number.Key & 1) == 0
select number.Value;
ObjectDumper.Write(evenNumbers);
}
}
4. 字符串
System.String看上去并不像是个集合,但它的确实现了IEnumerable<Char>接口,因此可以使用LINQ to Objects对其进行操作,示例如下:
class StringTest
{
static void Main()
{
var count =
"Do you know that man? I can't remember him!"
.Where(c => !Char.IsLetter(c))
.Count();
Console.WriteLine(count);
}
}
输出:11
注意:智能感知不会自动为String给出LINQ相关的扩展方法,因为很少有人将String作为IEnumerable<Char>处理
5. 其他集合
可以用LINQ to Objects与其他任何实现了IEnumerable<T>接口的集合配合使用,非泛型的集合实现了IEnumerable接口,却没有实 现IEnumerable<T>接口,不过可以借助Cast和OfType查询操作符对非泛型集合进行查询,这个以后再谈
上面的例子都是控制台应用程序,下面分别用ASP.NET和Windows Forms举两个例子
ASP.NET示例如下:
查询代码:
var books = new Book[]{
new Book {Title="LINQ in Action", Price=65, Authors=new String[]{"Fabrice Marguerie","Steve Eichert","Jim Wooley"}},
new Book {Title="CLR Via C#", Price=95, Authors=new String[]{"Jeffrey Richter"}},
new Book {Title="C# in depth", Price=69, Authors=new String[]{"Jon Skeet"}},
new Book {Title="ASP.NET AJAX in Action", Price=59, Authors=new String[]{"Alessandro Gallo","David Barkol","Rama Krishna Vavilala"}},
new Book {Title="Essential C# 4.0", Price=89, Authors=new String[]{"Mark Michaelis"}},
new Book {Title="NHibernate in Action", Price=55, Authors=new String[]{"Pierre Henri Kuate","Tobin Harris","Christian Bauer","Gavin King"}}
};
GridView1.DataSource =
from book in books
where book.Title.Length > 15
orderby book.Price
select new { book.Title, FirstAuthor = book.Authors[0] };
GridView1.DataBind();
页面上添加一个GridView:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="true">
</asp:GridView>
运行效果如下图所示:
注意:在上述代码中,返回了包含三个属性的匿名类型的集合,这三个属性的类型将由编译器根据属性的值得出,分别为String、Decimal和 String;GridView通过反射得到数据源中对象的各个属性,因此自动生成的列的顺序是无法控制的,不能认为生成列的顺序一定会与绑定对象中属性 的定义顺序保持一致;匿名类型带来的好处:借助于匿名类型,无需在GridView中显式指定列名,程序无需为了组合数据而创建专门的类型,在某些情况 下,甚至能够通过匿名类型将领域模型映射成表现层模型
Windows Forms示例如下:
在窗体上添加一个DataGridView控件,添加如下代码:
using System;
using System.Linq;
using System.Windows.Forms;
namespace WinFormTest
{
public sealed partial class FormMain : Form
{
private static volatile FormMain form;
private static Object objLock = new Object();
private FormMain()
{
InitializeComponent();
}
private void FormMain_Load(object sender, EventArgs e)
{
String[] books = { "LINQ in Action", "CLR Via C#", "C# in depth", "ASP.NET AJAX in Action",
"Essential C# 4.0", "NHibernate in Action", "jQuery in Action", "Expert C# 2008 Business Objects"};
var query =
from book in books
where book.Length > 18
orderby book
select new { Book = book };
dataGridView1.DataSource = query.ToList();
}
//单例模式,使用双重锁定
public static FormMain Instance
{
get
{
if (form == null)
{
lock (objLock)
{
if (form == null)
{
form = new FormMain();
}
}
}
return form;
}
}
}
}
运行效果如下图所示:
注意:这里通过匿名类型封装了图书的标题,因为DataGridView控件会默认显示绑定对象的属性,因此若是直接将图书的标题以字符串的形式绑定上去 的话,看到的将是这个字符串的长度,因为Length属性是字符串的唯一属性(可以尝试将查询表达式的最后一句 select new { Book = book } 改为 select book 查看结果);返回的结果序列被转化为一个列表,这是DataGridView控件在进行数据绑定时所必需的,否则要借助于BindingSource对象 来完成绑定