我在项目中使用LINQ有一些问题/疑虑。第一个问题是 - 旧(select Item from..
)linq和新版本(.Select(r => ..)
)之间的性能是否存在差异?
第二个问题,如何翻译LINQ表达(以及在什么中)?它会先翻译成旧语法,然后翻译成其他东西(中间语言)吗?
答案 0 :(得分:6)
我们可以编写linq查询的两种方式之间没有任何区别。
具体来说,这个
var adults = from customer in customers
where customer.Age>18
select customer;
相当于:
var adults = customers.Where(customer=>customer.Age>18);
实际上,编译器会将第一个查询转换为第二个查询。编写linq查询的第一种方法就像语法糖。在引擎盖下,如果您编译代码然后使用dissasembler查看IL代码,您会注意到您的查询已被翻译为上述第二种形式。
使用第一种方式编写的查询,我们说我们已经使用了查询语法。在使用第二种方式编写查询时,我们说我们使用了流畅的语法。
答案 1 :(得分:1)
旧版本(
select Item from..
)和新版本(.Select(r => ..)
)之间的性能是否存在差异?
这些都不比另一个年长,因为两者同时进入语言。如果任何.Select()
可以被认为是较旧的,那么方法调用几乎总是调用扩展方法(因此只能从.NET 3.5开始,并且只能用C#3.0调用),一般都有方法调用从1.0开始。
表现没有区别,因为他们说同样的话是不同的方式。 (它可能是你可以找到一个案例导致一个但不是另一个的冗余,但大多数情况下这些冗余被编译器捕获并被删除)。
如何翻译LINQ表达式(以及在何处)?它会先翻译成旧语法,然后翻译成其他东西(中间语言)吗?
考虑到,根据上述情况,from item in someSource select item.ID
和someSouce.Select(item => item.ID)
是相同的。编译器必须做两件事:
这两者齐头并进。第一部分与任何其他方法调用相同:
查找在someSource
类型上定义的名为Select()
的方法,并采用适当类型的一个参数(我将来到"适当的类型& #34;一分钟内。
如果找不到方法,请查找在someSource
类型的直接基础上定义的方法,依此类推,直到您没有更多要检查的基类(到达{{1}之后) })。
如果没有找到方法,请查找在静态类上定义的扩展方法,该类可以通过object
使用,其第一个(using
)参数的类型为{ {1}},以及我说的相应类型的第二个参数我将在一分钟后回来。
如果找不到方法,请查找可以接受this
类型和lambda作为参数的通用扩展方法。
如果没有找到方法,请对someSource
的基本类型及其实现的接口执行以上两个步骤,继续进一步扩展这些接口的基类型或接口。
如果未找到方法,则引发编译器错误。同样,如果上述任何步骤在同一步骤中发现两个或更多同等适用方法 ,则会引发编译错误。
到目前为止,这与someSource
调用someSource
上定义的"".IsNormalized()
方法的方式相同,IsNormalized()
调用string
上定义的"".GetHashCode()
方法(虽然后面的步骤表示GetHashCode()
上定义的覆盖是实际执行的内容),object
调用string
上定义的"".GetType()
方法。
事实上,我们可以在下面看到这一点:
GetType()
此处因为object
有自己适用的public class WeirdSelect
{
public int Select<T>(Func<WeirdSelect, T> ignored)
{
Console.WriteLine("Select Was Called");
return 2;
}
}
void Main()
{
int result = from whatever in new WeirdSelect() select whatever;
}
方法,而不是WeirdSelect
和Select
中定义的扩展方法之一。
现在,我手动挥动了#34;相应类型的参数&#34;上面因为lambdas带来的一个复杂因素是C#代码中的lambda可以变成委托(在这种情况下是Enumerable
,其中Queryable
是lambdas参数的类型而Func<TSource, TResult>
1}}它返回的值的类型)或生成的CIL代码中的表达式(在本例中为TSource
)。
因此,方法调用解析正在寻找接受TResult
(或类似委托)的方法或接受Expression<Func<TSource, TResult>>
(或类似表达式)的方法。如果它在搜索的同一阶段发现,则会出现编译错误,因此以下操作无效:
Func<TSource, TResult>
现在,99.999%的时间我们使用Expression<Func<TSource, TResult>>
来实现public class WeirdSelect
{
public int Select<T>(Func<WeirdSelect, T> ignored)
{
Console.WriteLine("Select Was Called");
return 2;
}
public int Select<T>(Expression<Func<WeirdSelect, T>> ignored)
{
Console.WriteLine("Select Was Called on expression");
return 1;
}
}
void Main()
{
int result = from whatever in new WeirdSelect() select whatever;
}
或实现select
的东西。如果它实现IQueryable<T>
,则方法调用解析将在IEnumerable<T>
中找到IQueryable<T>
,如果它实现public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
,则会在Queryable
中找到IEnumerable<T>
}。 public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
派生自Enumerable
并不重要,因为在IQueryable<T>
被视为基本接口之前,其方法将在上述过程的早期步骤中找到。< / p>
因此,99.999%的时间会对这两种扩展方法之一进行调用。在IEnumerable<T>
的情况下,lambda变成了一些产生适当IEnumerable<T>
的代码,然后传递给该方法(然后查询引擎能够将其转换为适当的代码,例如创建适当的SQL查询是否为数据库支持的查询引擎,否则为其他内容。在IQueryable<T>
的情况下,lamda被转换为一个匿名委托,它被传递给有点像这样的方法:
Expression
回到你的问题:
首先将它翻译成旧语法然后翻译成其他语言(中间语言)吗?
您可以将较新的IEnumerable<T>
语法视为&#34;变为&#34;较旧的public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
//Simplifying a few things, this code is to show the idea only
foreach(var item in source)
yield return selector(item);
}
语法(但不是很老,因为它在99%的时间内依赖于扩展方法),因为它使方法调用更清晰,但实际上它们相同。在CIL中产生的差异取决于调用是实例方法还是(几乎总是如此)扩展方法,甚至更多是关于lambda是用于生成表达式还是委托。