【More Effective C#】LINQ表達式與方法調用的映射
2010-10-15 08:05 空逸云 閱讀(982) 評論(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
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
浙公網安備 33010602011771號