《C#本质论》读书笔记(15)使用查询表达式的LINQ
2016-11-21 11:33
337 查看
15.1 查询表达式的概念
简单的查询表达式
private static void ShowContextualKeywords1()
{
IEnumerable<string> selection = from word in Keywords
where !word.Contains('*')
select word;
foreach (string keyword in selection)
{
Console.Write(" " + keyword);
}
}
private static string[] Keywords = {
"abstract", "add*", "alias*", "as", "ascending*", "base",
"bool", "break", "by*", "byte", "case", "catch", "char",
"checked", "class", "const", "continue", "decimal",
"default", "delegate", "descending*", "do", "double",
"dynamic*", "else", "enum", "event", "equals*",
"explicit", "extern", "false", "finally", "fixed",
"from*", "float", "for", "foreach", "get*", "global*",
"group*", "goto", "if", "implicit", "in", "int",
"into*", "interface", "internal", "is", "lock", "long",
"join*", "let*", "namespace", "new", "null", "object",
"on*", "operator", "orderby*", "out", "override",
"params", "partial*", "private", "protected", "public",
"readonly", "ref", "remove*", "return", "sbyte", "sealed",
"select*", "set*", "short", "sizeof", "stackalloc",
"static", "string", "struct", "switch", "this", "throw",
"true", "try", "typeof", "uint", "ulong", "unchecked",
"unsafe", "ushort", "using", "value*", "var*", "virtual",
"void", "volatile", "where*", "while", "yield*"
};
Directory.GetCurrentDirectory()
public static void Main()
{
List1(Directory.GetCurrentDirectory(),"*");
}
static void List1(string rootDirectory, string searchPattern)
{
IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
select new FileInfo(fileName);
foreach (FileInfo file in files)
{
Console.WriteLine(".{0} ({1})",
file.Name, file.LastWriteTime);
}
}
这里返回的是一个
C#3.0引入匿名类型,很大程度上就是利用像这样的“投射”功能。var files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
select new FileInfo(fileName);
where !word.Contains('*')
select word;
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
orderby (new FileInfo(fileName)).Length descending,
fileName
select fileName;
ascending和descending是上下文关键字,分别是升序或降序排序。
{
ListByFileSize2(Directory.GetCurrentDirectory(), "*");
}
static void ListByFileSize2(
string rootDirectory, string searchPattern)
{
IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
orderby new FileInfo(fileName).Length, fileName
select new FileInfo(fileName);
foreach (FileInfo file in files)
{
// As simplification, current directory is
// assumed to be a subdirectory of
// rootDirectory
string relativePath = file.FullName.Substring(
Environment.CurrentDirectory.Length);
Console.WriteLine(".{0}({1})",
relativePath, file.Length);
}
}
可以用let子句避免这种昂贵的开销。IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
let file = new FileInfo(fileName)
orderby file.Length, fileName
select file;
let 解释let子句引入了一个新的范围变量
它容纳的表达式值可以在查询表达式剩余部分使用
可以添加任意数量的let表达式,只需要它们每一个作为一个附加的子句
放在第一个from子句之后,最后一个select/group by子句之前,加入查询即可
{
IEnumerable<IGrouping<bool, string>> selection =
from word in keyWords
group word by word.Contains('*');
foreach (IGrouping<bool, string> wordGroup
in selection)
{
Console.WriteLine(Environment.NewLine + "{0}:",
wordGroup.Key ?
"Contextual Keywords" : "Keywords");
foreach (string keyword in wordGroup)
{
Console.Write(" " +
(wordGroup.Key ?
keyword.Replace("*", null) : keyword));
}
}
}
结果
查询结果是一系列IGrouping<bool, string>类型元素。
查询生成一系列分组,将相同的bool类型键应用于组内的每个string。
在group子句后面选择一个匿名类型private static void GroupKeywords1()
{
IEnumerable<IGrouping<bool, string>> keywordGroups =
from word in keyWords
group word by word.Contains('*');
var selection = from groups in keywordGroups
select new
{
IsContextualKeyword = groups.Key,
Items = groups
};
foreach (var wordGroup in selection)
{
Console.WriteLine(Environment.NewLine + "{0}:",
wordGroup.IsContextualKeyword ?
"Contextual Keywords" : "Keywords");
foreach (var keyword in wordGroup.Items)
{
Console.Write(" " +
keyword.Replace("*", null));
}
}
}
IGrouping<TKey,TElement>.Key 重命名为IsContextualKeyword,并命名了子集合属性Items。有人人为可以在匿名类型中添加一个属性来标识数据项的个数,然后,这个功能由wordGroup.Items.Count()提供。
from word in keyWords
group word by word.Contains('*')
into groups
select new
{
IsContextualKeyword = groups.Key,
Items = groups
};
into相当于一个管道操作符,它将第一个查询结果“管道传送”给第二个查询。用这种方式可以链接起任意数量的查询。
var selection = from word in keyWords
from character in word
select character;
还可以用多个from子句生成笛卡尔积——几个序列所有可能的组合。var numbers = new[] {1, 2, 3};
var product = from word in keyWords
from number in numbers
select new {word, number};
typeof(Enumerable).GetMembers()返回System.Linq.Enumerable的所有成员(方法、属性等)的一个列表。但许多成员都是重载。
15.2 查询表达式作为方法调用查询表达式未对CLR或CIL语言进行任何改动。由C#编译器将查询表达式转换成一系列方法调用。IEnumerable<string> selection = from word in Keywords
where !word.Contains('*')
select word;
编译之后,会转换成一个由System.Linq.Enumerable提供的IEnumerable<T>扩展方法调用IEnumerable<string> selection =
keyWords.Where(word => word.Contains('*'));
如14章所述,Lambda表达式随后由编译器转换成一个带有Lambda的主体的方法。Lambda表达式的作用变成分配一个对该方法的委托。 每个查询表达式都能转换成方法调用,但不是每一系列的方法调用都有对应的查询表达式。例如,扩展方法TakeWhile<T>(Func<T,bool> predicate)就没有与之等价的查询表达式,这个扩展方法只要谓词返回true,就反复返回集合中的项。
来自为知笔记(Wiz)
简单的查询表达式
private static void ShowContextualKeywords1()
{
IEnumerable<string> selection = from word in Keywords
where !word.Contains('*')
select word;
foreach (string keyword in selection)
{
Console.Write(" " + keyword);
}
}
private static string[] Keywords = {
"abstract", "add*", "alias*", "as", "ascending*", "base",
"bool", "break", "by*", "byte", "case", "catch", "char",
"checked", "class", "const", "continue", "decimal",
"default", "delegate", "descending*", "do", "double",
"dynamic*", "else", "enum", "event", "equals*",
"explicit", "extern", "false", "finally", "fixed",
"from*", "float", "for", "foreach", "get*", "global*",
"group*", "goto", "if", "implicit", "in", "int",
"into*", "interface", "internal", "is", "lock", "long",
"join*", "let*", "namespace", "new", "null", "object",
"on*", "operator", "orderby*", "out", "override",
"params", "partial*", "private", "protected", "public",
"readonly", "ref", "remove*", "return", "sbyte", "sealed",
"select*", "set*", "short", "sizeof", "stackalloc",
"static", "string", "struct", "switch", "this", "throw",
"true", "try", "typeof", "uint", "ulong", "unchecked",
"unsafe", "ushort", "using", "value*", "var*", "virtual",
"void", "volatile", "where*", "while", "yield*"
};
15.1.1 投射
查询表达式输出是一个IEnumerbale<T>或
IQueryable<T>集合。T数据类型是从select或者groupby子句推导。上例string数据类型是从
select word推导的,因为word是一个字符串。word数据类型是由
from子句所指定的IEnumerbale<T>集合的类型参数(这里是Keywords)。由于Keywords是一个string数组,它实现了
IEnumerbale<T>,所以word是一个字符串。表达式查询特定类型集合时,结果允许将数据投射成一个完全不同的类型。
Directory.GetCurrentDirectory()
public static void Main()
{
List1(Directory.GetCurrentDirectory(),"*");
}
static void List1(string rootDirectory, string searchPattern)
{
IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
select new FileInfo(fileName);
foreach (FileInfo file in files)
{
Console.WriteLine(".{0} ({1})",
file.Name, file.LastWriteTime);
}
}
这里返回的是一个
IEnumerable<FileInfo>,而不是
System.IO.Directory.GetFiles()返回的
IEnumerables<string>数据类型。
C#3.0引入匿名类型,很大程度上就是利用像这样的“投射”功能。var files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
select new FileInfo(fileName);
15.1.2 筛选
where子句在垂直方向筛选集合。 IEnumerable<string> selection = from word in Keywordswhere !word.Contains('*')
select word;
15.1.3 排序
在查询表达式中对数据进行排序的是 orderby 子句。IEnumerable<string> fileNames =from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
orderby (new FileInfo(fileName)).Length descending,
fileName
select fileName;
ascending和descending是上下文关键字,分别是升序或降序排序。
15.1.4 let子句
下面代码与上面的代码相似。问题是FileInfo要创建两次,分别在orderby 和 select子句中创建。public static void Main(){
ListByFileSize2(Directory.GetCurrentDirectory(), "*");
}
static void ListByFileSize2(
string rootDirectory, string searchPattern)
{
IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
orderby new FileInfo(fileName).Length, fileName
select new FileInfo(fileName);
foreach (FileInfo file in files)
{
// As simplification, current directory is
// assumed to be a subdirectory of
// rootDirectory
string relativePath = file.FullName.Substring(
Environment.CurrentDirectory.Length);
Console.WriteLine(".{0}({1})",
relativePath, file.Length);
}
}
可以用let子句避免这种昂贵的开销。IEnumerable<FileInfo> files =
from fileName in Directory.GetFiles(
rootDirectory, searchPattern)
let file = new FileInfo(fileName)
orderby file.Length, fileName
select file;
let 解释let子句引入了一个新的范围变量
它容纳的表达式值可以在查询表达式剩余部分使用
可以添加任意数量的let表达式,只需要它们每一个作为一个附加的子句
放在第一个from子句之后,最后一个select/group by子句之前,加入查询即可
15.1.5 分组
SQL中涉及对数据项进行聚合以生成一个汇总或合计或其他聚合值。LINQ中表达力更强,LINQ允许将单独的项分组到一系列子集合中,还允许那些组与所查的集合中项关联private static void GroupKeywords1(){
IEnumerable<IGrouping<bool, string>> selection =
from word in keyWords
group word by word.Contains('*');
foreach (IGrouping<bool, string> wordGroup
in selection)
{
Console.WriteLine(Environment.NewLine + "{0}:",
wordGroup.Key ?
"Contextual Keywords" : "Keywords");
foreach (string keyword in wordGroup)
{
Console.Write(" " +
(wordGroup.Key ?
keyword.Replace("*", null) : keyword));
}
}
}
结果
查询结果是一系列IGrouping<bool, string>类型元素。
查询生成一系列分组,将相同的bool类型键应用于组内的每个string。
在group子句后面选择一个匿名类型private static void GroupKeywords1()
{
IEnumerable<IGrouping<bool, string>> keywordGroups =
from word in keyWords
group word by word.Contains('*');
var selection = from groups in keywordGroups
select new
{
IsContextualKeyword = groups.Key,
Items = groups
};
foreach (var wordGroup in selection)
{
Console.WriteLine(Environment.NewLine + "{0}:",
wordGroup.IsContextualKeyword ?
"Contextual Keywords" : "Keywords");
foreach (var keyword in wordGroup.Items)
{
Console.Write(" " +
keyword.Replace("*", null));
}
}
}
IGrouping<TKey,TElement>.Key 重命名为IsContextualKeyword,并命名了子集合属性Items。有人人为可以在匿名类型中添加一个属性来标识数据项的个数,然后,这个功能由wordGroup.Items.Count()提供。
15.1.6 使用into进行查询延续
上面的代码,一个现有的查询可以作为另一个查询的输入。然而,我们可以使用into通过查询延续子句来扩展任何查询。查询延续是一个语法糖,能简单的表示“创建量个查询并将第一个用作第二个的入口”。var selection =from word in keyWords
group word by word.Contains('*')
into groups
select new
{
IsContextualKeyword = groups.Key,
Items = groups
};
into相当于一个管道操作符,它将第一个查询结果“管道传送”给第二个查询。用这种方式可以链接起任意数量的查询。
15.1.7 用多个from 子句“平整”序列的序列
经常将一个序列的序列“平整”成单个序列。一系列客户中的每个客户都可能关联一系列订单,或者一系列目录中的每个目录都关联了一系列文件。SelectMany序列操作符(14章讨论过)可以连接所有的子序列;要用查询表达式做相同的事情,可以用多个from子句var selection = from word in keyWords
from character in word
select character;
还可以用多个from子句生成笛卡尔积——几个序列所有可能的组合。var numbers = new[] {1, 2, 3};
var product = from word in keyWords
from number in numbers
select new {word, number};
主题:不重复的成员
查询表达式没与专门的语法,但是,可以结合查询操作符Distict()来实现。typeof(Enumerable).GetMembers()返回System.Linq.Enumerable的所有成员(方法、属性等)的一个列表。但许多成员都是重载。
15.2 查询表达式作为方法调用查询表达式未对CLR或CIL语言进行任何改动。由C#编译器将查询表达式转换成一系列方法调用。IEnumerable<string> selection = from word in Keywords
where !word.Contains('*')
select word;
编译之后,会转换成一个由System.Linq.Enumerable提供的IEnumerable<T>扩展方法调用IEnumerable<string> selection =
keyWords.Where(word => word.Contains('*'));
如14章所述,Lambda表达式随后由编译器转换成一个带有Lambda的主体的方法。Lambda表达式的作用变成分配一个对该方法的委托。 每个查询表达式都能转换成方法调用,但不是每一系列的方法调用都有对应的查询表达式。例如,扩展方法TakeWhile<T>(Func<T,bool> predicate)就没有与之等价的查询表达式,这个扩展方法只要谓词返回true,就反复返回集合中的项。
来自为知笔记(Wiz)
相关文章推荐
- C# 温故知新 基础篇(15) 使用LINQ查询表达式<思维导图>
- NHibernate初学者指南(15):使用LINQ to NHibernate提供程序查询数据
- C#使用LINQ查询表达式的基本子句总结
- LINQ查询表达式中使用对象初始化器
- [深入学习C#]LINQ查询表达式详解(1)——基本语法、使用扩展方法和Lambda表达式简化LINQ查询
- 第十五章 使用查询表达式的LINQ
- linq 扩展,在查询中使用字符串表达式对结果进行排序
- 十五、C# 使用查询表达式的LINQ
- 查询表达式(LINQ)简介及VS2008下的使用
- linq 实现group by 不使用group关键字 等同lambad表达式中的group join 查询一对多关系
- (14)使用查询表达式的LINQ
- Lambda表达式--使用方法语法的复杂查询: join (在单个 LINQ to Entities 查询中的两个结构上不兼容的初始化过程中出现类型)
- 深入理解 c# 第一章 使用LINQ 查询表达式
- 在LINQ to SQL中使用Translate方法以及修改查询用SQL 推荐
- [引]LINQ 查询表达式(C# 编程指南)
- Python天天美味(15) - Python正则表达式操作指南(re使用)(转)
- T_SQL中使用正则表达式匹配查询结果
- [引]LINQ 查询表达式(C# 编程指南)
- 微软免费图书《Introducing Microsoft LINQ》翻译Chapter2.1:C# 3.0 特性(对象初始化表达式\匿名类型\查询表达式)
- Linq 此提供程序只支持对返回实体或投影(包含所有标识列)的有序查询使用 问题的解决