博客园  :: 首页  :: 联系 :: 管理

C# 2008 学习笔记 - LINQ基础(一)- LINQ概念

Posted on 2008-02-21 16:01  sunrack  阅读(1022)  评论(1编辑  收藏  举报
一、LINQ 查询表达式(query expressions )可以使用统一的方式对实现IEnumberable<T>接口的对象、关系数据库、数据集(DataSets)以及XML文档进行访问。
     严格的说,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);
}

其中的g可以是任何变量名

IEnumerable<string> subset = from game in currentVideoGames
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);
}


会发现,变量subset 的类型实际上是OrderedEnumerable<TElement, TKey>,(在CIL代码中显示为OrderedEnumerable`2),这是在程序集System.Core.dll 中定义的内部抽象类型(internal abstract)。

六、LINQ和隐式局部变量
LINQ查询结果集的类型在不同的LINQ相关的命名空间中,可以是不同的类型,因此很难给出准确的类型。
举例
static void QueryOverInts()
{
int[] numbers = {102030401238};
// 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);

运行结果显示,subset的类型是<WhereIterator>d__0^1

可见LINQ表达式的形式不同,其结果类型就不同,都是唯一的。

上面的事例中,由于IEnumerable<T>实现了非泛函数IEnumerable接口,所以可以写为
System.Collections.IEnumerable subset =
from i 
in numbers where i < 10 select i;

根据以上分析,在获取LINQ的返回结果时,最好使用 var 关键字。

static void QueryOverInts()
{
int[] numbers = {102030401238};
// 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);
}

七、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
{

}

但是,它间接的通过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 = { 102030401238 };

// 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 = { 102030401238 };
// 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();

十、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;
}

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

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

十二、使用OfType<T>()过滤数据

非泛型集合类型的元素可以是多种不同的数据类型,因为它的元素类型是object。如果要得到该集合中某种数据类型的元素的子集,就可以使用OfType<T>()来过滤。

// Extract the ints from the ArrayList.
ArrayList myStuff = new ArrayList();
myStuff.AddRange(
new object[] { 104008falsenew 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);
}