我想在我的类上启用LinQ查询语法。我认为查询语法被转换为方法语法,例如:
var query = from p in new Class1<Product>()
where p.Id == "1000"
select p
被翻译为:
var query = new Class1<Product>().Where(p => p.Id == "1000").Select(p => p);
然后我将Class1
实现为:
public class Class1<T>
{
public Class1<T> Where(Expression<Func<T, bool>> expression)
{
return this;
}
public Class1<T> Select<TResult>(Func<T, TResult> expression)
{
return this;
}
}
我用这段代码测试了它:
static void Main(string[] args)
{
var query = from p in new Class1<Product>()
where p.Id == "1000"
select p;
}
然后我注意到Select
方法没有被调用,但是如果我从LinQ where
删除Select
clausule则被调用:
static void Main(string[] args)
{
var query = from p in new Class1<Product>()
// where p.Id == "1000" -> commenting that Select method is called
select p;
}
有人知道为什么吗?
这是一个可以测试它的小提琴:https://dotnetfiddle.net/JgxKG9
答案 0 :(得分:9)
有人知道为什么吗?
是的,因为那是语言规范所说的。查询表达式转换全部在C#5规范的7.16.2节中。
第7.16.2.5节解释了为什么您的初始示例不正确 - Select
未被调用:
表单
的查询表达式from x in e select v
被翻译成
( e ) . Select ( x => v )
除非 v 是标识符
x
,否则翻译只是( e )
例如
from c in customers.Where(c => c.City == “London”) select c
简单地翻译成
customers.Where(c => c.City == “London”)
然而,退化查询表达式的情况并非如7.16.2.3所述 - 这解释了删除where
子句时会发生什么:
表单
的查询表达式from x in e select x
被翻译成
( e ) . Select ( x => x )
示例
from c in customers select c
被翻译成
customers.Select(c => c)
简并查询表达式是一个简单地选择源元素的表达式。翻译的后期阶段通过用其源替换它们来移除由其他翻译步骤引入的退化查询。但是,确保查询表达式的结果永远不是源对象本身是很重要的,因为这会向查询客户端显示源的类型和标识。因此,此步骤通过在源上显式调用Select来保护直接在源代码中编写的退化查询。然后由
Select
和其他查询运算符的实现者来确保这些方法永远不会返回源对象本身。
答案 1 :(得分:6)
您的理解有点不正确,以下查询:
var query = from p in new Class1<Product>()
where p.Id == "1000"
select p
将翻译为:
var query = new Class1<Product>().Where(p => p.Id == "1000");
当您删除where
部分时:
var query = from p in new Class1<Product>()
select p;
现在它将被翻译为:
var query = new Class1<Product>().Select(p=>p);
答案 2 :(得分:5)
我很确定从查询语法到方法语法的转换会优化对Select
的调用,如果它声明了身份投影。
由于p => p
会将所有内容投射到自身,而Where
子句已经在源序列和结果之间添加了一个抽象层,因此不再需要此调用。
所以
var query = from p in new Class1<Product>()
where p.Id == "1000"
select p;
仅翻译为
var query = new Class1<Product>().Where(p => p.Id == "1000");
但我承认我只是猜测而且仍在寻找相关的规格部分 更新:Jon更快