【More Effective C#】LINQ表达式与方法调用的映射
2010-10-15 08:05 空逸云 阅读(975) 评论(2) 编辑 收藏 举报LINQ构建在两个概念之上,一种查询语言和一系列将查询语言转换成方法调用的实现.在编译时,编译器将LINQ表达式(LINQ to object)转换成方法调用.
.Net基础类库提供了两种扩展方法.System.Linq.Enumerable使用了IEnumerable<T>上扩展来实现,而System.Linq.Queryable则提供了类似的一系列IQueryable<T>上的扩展.两者的转换略为不同.前者在编译时转换成相应的扩展方法调用.而后者则能将LINQ表达式转换成SQL查询,并有SQL数据库引擎执行.
从LINQ表达式到方法调用的转换时一个复杂的迭代过程.编译器在转换时也有一个特定的顺序.
例如
int[] someNumbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var answer = from n in someNumbers
where n < 5
select n;
最后将转换成
int[] someNumbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var answer =someNumbers.Where(n=>n<5);
可以看到.在上面的转换后.Select被优化去掉了.这就是一个退化选择.不止是Where和Select.相应的LINQ表达式都会被转换成相应的扩展方法..
编译器的转换
C#编译器将把查询和lambda表达式(Linq to object)转换成静态委托,实力委托和闭包.
int[] someNumbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var answer = from n in someNumbers
select n;
把上边的代码编译.使用Refletor打开程序集.查看生成的相关代码.
int[] someNumbers = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
CS$<>9__CachedAnonymousMethodDelegate1 = new Func<int, int>(null, (IntPtr) <Main>b__0);
}
IEnumerable<int> answer = Enumerable.Select<int, int>(someNumbers, CS$<>9__CachedAnonymousMethodDelegate1);
可以看到.编译器自动生成了一个委托.下面.使用更为直观的代码描述.生成的代码大致如下:
private static int HiddenFuc(int n)
{
return n * n;
}
private static Func<int, int> HiddenDelegateDefinition
int[] someNumbers = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
if (HiddenDelegateDefinition == null)
{
HiddenDelegateDefinition = new Func<int, int>(HiddenFuc);
}
IEnumerable<int> anser = someNumbers.Select<int, int>(HiddenDelegateDefinition);
可以看到.编译器生成了一个静态方法实现Select表达式.并且通过委托实现查询.
实际上.上述的lambda表达式的主体部分并没有访问任何实例变量或是局部变量.什么是实例变量.什么又是局部变量.?
访问实例变量的Lambe表达式
public class ModFilter
{
private readonly int modulus;
public ModFilter(int mod)
{
modulus = mod;
}
public IEnumerable<int> FindValues(IEnumerable<int> sequence)
{
return from n in sequence
where n % modulus == 0 //访¤问实例变量
select n * n;
}
}
编译器将会为你生成一个实例方法,生成的代码大致如下
public class ModFilter
{
private readonly int modulus;
public ModFilter(int mod)
{
modulus = mod;
}
private bool WhereClause(int n)
{
return (n % modulus) == 0;
}
private static int SelectClause(int n)
{
return n * n;
}
private static Func<int, int> SelectDelegate;
public IEnumerable<int> FindValues(IEnumerable<int> sequence)
{
if (SelectDelegate == null)
{
SelectDelegate = new Func<int, int>(SelectClause);
}
return sequence.Where<int>(new Func<int, bool>(this.WhereClause)).Select<int, int>(SelectClause);
}
}
Lambda表达式访问实例变量
若是Lambda表达式中访问了外部方法的实例变量.则编译器将自动生成一个私有的嵌套类型.
public class ModFilterCloser
{
private readonly int modulus;
public ModFilterCloser(int mod)
{
modulus = mod;
}
public IEnumerable<int> FindValues(IEnumerable<int> sequence)
{
int numValues = 0;
return from n in sequence
where n % modulus == 0
select n * n / ++numValues; //调用了方法外的局部变量numValues
}
}
生成的代码大致如下.
public class ModFilterCloser
{
private sealed class Closure
{
public ModFilterCloser outer;
public int numValues;
public int SelectClause(int n)
{
return (n * n) / ++this.numValues;
}
}
private readonly int modulus;
public ModFilterCloser(int mod)
{
modulus = mod;
}
private bool WhereClause(int n)
{
return (n % modulus) == 0;
}
public IEnumerable<int> FindValues(IEnumerable<int> sequence)
{
Closure c = new Closure();
c.outer = this;
c.numValues = 0;
return sequence.Where<int>(new Func<int,bool>(this.WhereClause)).Select<int,int>(c.SelectClause));
}
}
LINQ to Sql 的实现
最后.要注意到.以上的转换实现都是在Linq to object中实现.即IEnumberale<T>中.而LINQ to SQL 的转换.编译后可以看到.并没发生任何变化.那是因为.只有在遍历迭代时,延迟执行.LINQ to SQL Provider才将LINQ表达式转换成SQL查询.
出处:http://kongyiyun.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。