严格的说,LINQ是用来描述一系列数据访问技术的术语。LINQ to Objects 适用于实现IEnumberable<T>接口的对象,LINQ to SQL 适用于关系数据库, LINQ to DataSet is 则是 LINQ to SQL的一个子集, LINQ to XML 适用于XML 文档。
二、LINQ 查询表达式 是强类型的(strongly typed),因此编译器会确保其语法正确性。
三、LINQ是一个可扩展技术,第三方可以使用扩展函数来定义新的查询操作符。
四、LINQ核心程序集(Assembly)
|
System.Core.dll |
Defines the types that represent the core LINQ API. This is the one assembly you must have access to. |
|
System.Data.Linq.dll |
Provides functionality for using LINQ with relational databases (LINQ to SQL). |
|
System.Data.DataSetExtensions.dll |
Defines a handful of types to integrate ADO.NET types into the LINQ programming paradigm (LINQ to DataSet). |
|
System.Xml.Linq.dll |
Provides functionality for using LINQ with XML document data (LINQ to XML). |
至少需要引用System.Linq 命名空间,在System.Core.dll中定义,Visual stuodio 2008默认会自动添加引用。
五、LINQ示例
static void QueryOverStrings()
{
// Assume we have an array of strings.
string[] currentVideoGames = {"Morrowind", "BioShock",
"Half Life 2: Episode 1", "The Darkness",
"Daxter", "System Shock 2"};
// Build a query expression to represent the items in the array
// that have more than 6 letters.
IEnumerable<string> subset = from g in currentVideoGames
where g.Length > 6 orderby g select g;
// Print out the results.
foreach (string s in subset)
Console.WriteLine("Item: {0}", s);
}
{
// Assume we have an array of strings.
string[] currentVideoGames = {"Morrowind", "BioShock",
"Half Life 2: Episode 1", "The Darkness",
"Daxter", "System Shock 2"};
// Build a query expression to represent the items in the array
// that have more than 6 letters.
IEnumerable<string> subset = from g in currentVideoGames
where g.Length > 6 orderby g select g;
// Print out the results.
foreach (string s in subset)
Console.WriteLine("Item: {0}", s);
}
其中的g可以是任何变量名
IEnumerable<string> subset = from game in currentVideoGames
where game.Length > 6 orderby game select game;
where game.Length > 6 orderby game select game;
如果调用下面的测试函数
static void ReflectOverQueryResults(object resultSet)
{
Console.WriteLine("***** Info about your query *****");
Console.WriteLine("resultSet is of type: {0}", resultSet.GetType().Name);
Console.WriteLine("resultSet location: {0}", resultSet.GetType().Assembly);
}
{
Console.WriteLine("***** Info about your query *****");
Console.WriteLine("resultSet is of type: {0}", resultSet.GetType().Name);
Console.WriteLine("resultSet location: {0}", resultSet.GetType().Assembly);
}
会发现,变量subset 的类型实际上是OrderedEnumerable<TElement, TKey>,(在CIL代码中显示为OrderedEnumerable`2),这是在程序集System.Core.dll 中定义的内部抽象类型(internal abstract)。
六、LINQ和隐式局部变量
LINQ查询结果集的类型在不同的LINQ相关的命名空间中,可以是不同的类型,因此很难给出准确的类型。
举例
static void QueryOverInts()
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Only print items less than 10.
IEnumerable<int> subset = from i in numbers where i < 10 select i;
foreach (int i in subset)
Console.WriteLine("Item: {0}", i);
ReflectOverQueryResults(subset);
}
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Only print items less than 10.
IEnumerable<int> subset = from i in numbers where i < 10 select i;
foreach (int i in subset)
Console.WriteLine("Item: {0}", i);
ReflectOverQueryResults(subset);
}
这里结果集合是通过调用 System.Linq.Enumerable.Where<T> 方法获得的。传入的第二个参数是由编译器生成的匿名方法,实际上是:
// The following LINQ query expression:
//
// IEnumerable<int> subset = from i in numbers where i < 10 select i;
//
// Is transformed into a call to the Enumerable.Where<int>() method:
//
IEnumerable<int> subset = Enumerable.Where<int>(numbers,
Program.<>9__CachedAnonymousMethodDelegate8);
//
// IEnumerable<int> subset = from i in numbers where i < 10 select i;
//
// Is transformed into a call to the Enumerable.Where<int>() method:
//
IEnumerable<int> subset = Enumerable.Where<int>(numbers,
Program.<>9__CachedAnonymousMethodDelegate8);
运行结果显示,subset的类型是<WhereIterator>d__0^1
可见LINQ表达式的形式不同,其结果类型就不同,都是唯一的。
上面的事例中,由于IEnumerable<T>实现了非泛函数IEnumerable接口,所以可以写为
System.Collections.IEnumerable subset =
from i in numbers where i < 10 select i;
from i in numbers where i < 10 select i;
根据以上分析,在获取LINQ的返回结果时,最好使用 var 关键字。
static void QueryOverInts()
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Use implicit typing here
var subset = from i in numbers where i < 10 select i;
//
and here.
foreach (var i in subset)
Console.WriteLine("Item: {0} ", i);
ReflectOverQueryResults(subset);
}
{
int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};
// Use implicit typing here
var subset = from i in numbers where i < 10 select i;
//
foreach (var i in subset)
Console.WriteLine("Item: {0} ", i);
ReflectOverQueryResults(subset);
}
七、LINQ和扩展函数
LINQ的背后,其实无缝集成了扩展函数。比如,System.Array 没有实现IEnumerable<T>接口
// The System.Array type does not seem to implement the correct
// infrastructure for query expressions!
public abstract class Array : ICloneable, IList, ICollection, IEnumerable
{

}
// infrastructure for query expressions!
public abstract class Array : ICloneable, IList, ICollection, IEnumerable
{
}
但是,它间接的通过System.Linq.Enumerable实现了很多LINQ需要的功能。System.Linq.Enumerable中实现了很多诸如Aggregate<T>(), First<T>(), Max<T>()等等的函数。在Visual studio 2008 智能提示中可以看到很多扩展函数。
八、LINQ的延迟执行(Differed Execution)
LINQ 查询表达式只有在迭代访问其内容时,才会被计算并执行。这样可以保证每次访问得到的是最新的数据。
static void QueryOverInts()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get numbers less than ten.
var subset = from i in numbers where i < 10 select i;
// LINQ statement evaluated here!
foreach (var i in subset)
Console.WriteLine("{0} < 10", i);
Console.WriteLine();
// Change some data in the array.
numbers[0] = 4;
// Evaluate again.
foreach (var j in subset)
Console.WriteLine("{0} < 10", j);
ReflectOverQueryResults(subset);
}
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get numbers less than ten.
var subset = from i in numbers where i < 10 select i;
// LINQ statement evaluated here!
foreach (var i in subset)
Console.WriteLine("{0} < 10", i);
Console.WriteLine();
// Change some data in the array.
numbers[0] = 4;
// Evaluate again.
foreach (var j in subset)
Console.WriteLine("{0} < 10", j);
ReflectOverQueryResults(subset);
}
在Visual studio 2008中,如果在LINQ查询执行之前的地方设置断点,则可在调试时,执行查询。
九、LINQ的立即执行
如果不想用foreach来迭代查询结果,则可以使用Enumerable定义的其他扩展函数。如ToArray<T>(), ToDictionary<TSource,TKey>(), and ToList<T>(),可以将查询结果直接放到强类型的结果集中,执行后,这些结果集就和查询表达式没有关系了,可以单独操作。
static void ImmediateExecution()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get data RIGHT NOW as int[].
int[] subsetAsIntArray =
(from i in numbers where i < 10 select i).ToArray<int>();
// Get data RIGHT NOW as List<int>.
List<int> subsetAsListOfInts =
(from i in numbers where i < 10 select i).ToList<int>();
}
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get data RIGHT NOW as int[].
int[] subsetAsIntArray =
(from i in numbers where i < 10 select i).ToArray<int>();
// Get data RIGHT NOW as List<int>.
List<int> subsetAsListOfInts =
(from i in numbers where i < 10 select i).ToList<int>();
}
在编译器可以明确推导出结果集的类型时,可以不指定类型
int[] subsetAsIntArray =
(from i in numbers where i < 10 select i).ToArray();
(from i in numbers where i < 10 select i).ToArray();
十、LINQ和泛型集合
LINQ可以对System.Collections.Generic命名空间上的类型进行查询
class Car
{
public string PetName = string.Empty;
public string Color = string.Empty;
public int Speed;
public string Make = string.Empty;
}
{
public string PetName = string.Empty;
public string Color = string.Empty;
public int Speed;
public string Make = string.Empty;
}
static void Main(string[] args)
{
Console.WriteLine("***** More fun with LINQ Expressions *****\n");
// Make a List<> of Car objects
// using object init syntax.
List<Car> myCars = new List<Car>() {
new Car{ PetName = "Henry", Color = "Silver", Speed = 100, Make = "BMW"},
new Car{ PetName = "Daisy", Color = "Tan", Speed = 90, Make = "BMW"},
new Car{ PetName = "Mary", Color = "Black", Speed = 55, Make = "VW"},
new Car{ PetName = "Clunker", Color = "Rust", Speed = 5, Make = "Yugo"},
new Car{ PetName = "Melvin", Color = "White", Speed = 43, Make = "Ford"}
};
}
{
Console.WriteLine("***** More fun with LINQ Expressions *****\n");
// Make a List<> of Car objects
// using object init syntax.
List<Car> myCars = new List<Car>() {
new Car{ PetName = "Henry", Color = "Silver", Speed = 100, Make = "BMW"},
new Car{ PetName = "Daisy", Color = "Tan", Speed = 90, Make = "BMW"},
new Car{ PetName = "Mary", Color = "Black", Speed = 55, Make = "VW"},
new Car{ PetName = "Clunker", Color = "Rust", Speed = 5, Make = "Yugo"},
new Car{ PetName = "Melvin", Color = "White", Speed = 43, Make = "Ford"}
};
}
LINQ查询
static void GetFastCars(List<Car> myCars)
{
// Create a query expression.
var fastCars = from c in myCars where c.Speed > 55 select c;
foreach (var car in fastCars)
{
Console.WriteLine("{0} is going too fast!", car.PetName);
}
}
{
// Create a query expression.
var fastCars = from c in myCars where c.Speed > 55 select c;
foreach (var car in fastCars)
{
Console.WriteLine("{0} is going too fast!", car.PetName);
}
}
// Create a query expression.
var fastCars = from c in myCars where
c.Speed > 90 && c.Make == "BMW" select c;
var fastCars = from c in myCars where
c.Speed > 90 && c.Make == "BMW" select c;
十一、LINQ和非泛型集合
虽然System.Collections命名空间没有实现IEnumerable<T>接口,但是可以通过Enumerable.OfType<T>()方法来实现LINQ的查询。
OfType<T>()是Enumerable中少数的没有用于扩展泛型类型的函数。
在实现IEnumerable接口的非泛型集合上调用该方法时,只要给出集合中元素的类型,就可以提取出实现IEnumerable<T>的对象。
static void Main(string[] args)
{
Console.WriteLine("***** LINQ over ArrayList *****\n");
// Here is a nongeneric collection of cars.
ArrayList myCars = new ArrayList() {
new Car{ PetName = "Henry", Color = "Silver", Speed = 100, Make = "BMW"},
new Car{ PetName = "Daisy", Color = "Tan", Speed = 90, Make = "BMW"},
new Car{ PetName = "Mary", Color = "Black", Speed = 55, Make = "VW"},
new Car{ PetName = "Clunker", Color = "Rust", Speed = 5, Make = "Yugo"},
new Car{ PetName = "Melvin", Color = "White", Speed = 43, Make = "Ford"}
};
// Transform ArrayList into an IEnumerable<T>-compatible type.
IEnumerable<Car> myCarsEnum = myCars.OfType<Car>();
// Create a query expression.
var fastCars = from c in myCarsEnum where c.Speed > 55 select c;
foreach (var car in fastCars)
{
Console.WriteLine("{0} is going too fast!", car.PetName);
}
}
{
Console.WriteLine("***** LINQ over ArrayList *****\n");
// Here is a nongeneric collection of cars.
ArrayList myCars = new ArrayList() {
new Car{ PetName = "Henry", Color = "Silver", Speed = 100, Make = "BMW"},
new Car{ PetName = "Daisy", Color = "Tan", Speed = 90, Make = "BMW"},
new Car{ PetName = "Mary", Color = "Black", Speed = 55, Make = "VW"},
new Car{ PetName = "Clunker", Color = "Rust", Speed = 5, Make = "Yugo"},
new Car{ PetName = "Melvin", Color = "White", Speed = 43, Make = "Ford"}
};
// Transform ArrayList into an IEnumerable<T>-compatible type.
IEnumerable<Car> myCarsEnum = myCars.OfType<Car>();
// Create a query expression.
var fastCars = from c in myCarsEnum where c.Speed > 55 select c;
foreach (var car in fastCars)
{
Console.WriteLine("{0} is going too fast!", car.PetName);
}
}
十二、使用OfType<T>()过滤数据
非泛型集合类型的元素可以是多种不同的数据类型,因为它的元素类型是object。如果要得到该集合中某种数据类型的元素的子集,就可以使用OfType<T>()来过滤。
// Extract the ints from the ArrayList.
ArrayList myStuff = new ArrayList();
myStuff.AddRange(new object[] { 10, 400, 8, false, new Car(), "string data" });
IEnumerable<int> myInts = myStuff.OfType<int>();
// Prints out 10, 400, and 8.
foreach (int i in myInts)
{
Console.WriteLine("Int value: {0}", i);
}
ArrayList myStuff = new ArrayList();
myStuff.AddRange(new object[] { 10, 400, 8, false, new Car(), "string data" });
IEnumerable<int> myInts = myStuff.OfType<int>();
// Prints out 10, 400, and 8.
foreach (int i in myInts)
{
Console.WriteLine("Int value: {0}", i);
}