博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
本书翻译目的为个人学习和知识共享,其版权属原作者所有,如有侵权,请告知本人,本人将立即对发帖采取处理。
允许转载,但转载时请注明本版权声明信息,禁止用于商业用途!
翻譯不妥之處,請指正!
博客园:CDplayer          相關鏈接:博客园:韩现龙 



語言集成是LINQ最基本的特徵。  在C# 3.0 和Visual Basic 9.0中,LINQ的查詢表達式最能讓你一下舊感受到這種新特性的不同。它允許你書寫像下面這樣的代碼︰

 var query =
    from    c in Customers
    where   c.Country == "Italy"
    orderby c.Name
    select  new { c.Name, c.City };


來替代下面的代碼:

 var query =
        Customers
        .Where( c => c.Country == "Italy" );
        .OrderBy( c => c.Name )
        .Select( c => new { c.Name, c.City } );

由于它使用了一種更加簡潔的語法描述了對數據的查詢,所以很多人把它稱作 “加糖的語法”。  不過,事實它并不像我們看到的這樣簡單,短短的幾行代碼卻是由很多語言機制與語法來支持的。 在這在這種表象下面,它運用到local type inference, extension methods, lambda expressions, object initialization expressions, 和 anonymous types等新技術。這些機制各自發揮著自己的作用, 但是如果你站在一個全局的角度來看這一切,你會發現它們在兩個方面都進了一大步︰1.使得代碼更加“宣告式”;2.降低數據和代碼之間的“阻抗不匹配”。



宣告式編程

SQL查詢和對應的C# 2.0或Visual Basic 8.0程序中的本地數據過濾操作之間的差別(如: tableor or  array for C# or Visual Basic)是什么?
用SQL,你會書寫以下代碼︰

 SELECT * FROM Customers WHERE Country = 'Italy'

用C#,你或許會這樣寫︰

 public List<Customer> ItalianCustomers( Customer customers[] )
 {
    List<Customer> result = new List<Customer>();
    foreach( Customer c in customers ) {
        if (c.Country == "Italy") result.Add( c );
    }
    return result;
}

C# 代碼花費很長時間讀與寫,重點是如何數據表達。  在SQL中,描述你想要“什麼”。  在C#中,描述“怎樣”獲得期望的結果。在SQL中,選擇最高效的算法去執行“怎樣”的操作 (在C#中表現的更明顯)的是查詢引擎的責任。這因為相比C#編譯器上的眾多限制, 面對“怎樣”的操作,SQL查詢引擎有更多的優化空間。
    注釋:
前面的C#代碼范例實現的功能在.NET 2.0中可以通過Find方法實現,但是對于大多數開發人員,必須的匿名代理語句就不太容易編寫了。  不過,范例僅僅為了表現不同編程間的差異。有了LINQ, C# 和Visual Basic就能編寫出更加具有“宣布式”風格的代碼了,它的查詢通過一種“宣布式“的結構描述基于數據的操作,而不是原有的重復、繁瑣的方式。  LINQ允許數據的業務邏輯更直接明了的體現出來, 同時這樣更加明了的業務邏輯是提供出更高水準服務的基礎。  

讓我來思考一下并行的問題:
一個SQL查詢可以被分成幾部門並行運算,完全因為它的數據表掃描算法不存在任何限制條件。  而一個C# foreach 循環很難被拆分為幾個循環幾部分被不用的進程并行執行。
    更多的訊息
并行的LINQ(PLINQ)是在并行方面的一個研究計劃,它可以通過LINQ編寫代碼來實現。宣告式編程可以很好的利用編譯器提供的服務能和架構,使得代碼更容易閱讀和維護。這應該是最重要的特性之一,因為它提高了程式員的開發效率。  例如,假定你想要得到一個應用程序中所有可用的靜態方法,返回一個IEnumerable<T>。
你能使用LINQ 通過反射機制,寫出下面的查詢︰
 
 var query =
    from    assembly in AppDomain.CurrentDomain.GetAssemblies()
    from    type in assembly.GetTypes()
    from    method in type.GetMethods()
    where   method.IsStatic
            && method.ReturnType.GetInterface( "IEnumerable`1" ) != null
    orderby method.DeclaringType.Name, method.Name
    group   method by new { Class = method.DeclaringType.Name,
                            Method = method.Name };

而等效的C# 代碼不僅更長、難以閱讀,可以還更容易產生一些錯誤。  讓我們看看一個沒有經過優化的C#代碼。
1-10:等價于前面LINQ查詢的C#代碼

 List<String> results = new List<string>();
foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
    foreach( var type in assembly.GetTypes() ) {
        foreach( var method in type.GetMethods()) {
            if (method.IsStatic &&
                method.ReturnType.GetInterface("IEnumerable`1") != null) {
                string fullName = String.Format( "{0}.{1}",
                                      method.DeclaringType.Name,
                                      method.Name );
                if (results.IndexOf( fullName ) < 0) {
                    results.Add( fullName );
                }
            }
        }
    }
}
results.Sort();



類型檢查

語言集成的另一個重要的特徵是類型檢查。當數據是由LINQ 操作返回的,那么它用不著再進行那些不安全的轉換。看似簡短的查詢表達式并沒有忽略類型檢查︰數據都是強類型,無論被查詢出來的集合或者返回值中的單個實體。在支持LINQ(目前C# 3.0 和Visual Basic 9.0)的語言中,類型檢查存在于LINQ每一個細節處理過程中。這使得在編寫LINQ查詢時,Visual Studio的特性(如IntelliSense和Refactoring)都是可以使用的,而這些Visual Studio特性對於程式員的開發效率是有著重要影響的。



透明化不同的類型系統

當你考慮Microsoft .NET Framework和SQLServer的類型系統時,你已經意識到他們的不同。  
使用LINQ時,你應賦予.NET類型系統更高的優先級,因為這類型系統是所有提供LINQ的開發語言(C# 3.0 和Visual Basic 9.0)都支持的。或許你會覺得有疑問:大多數的數據都是存放在關系型數據庫中,同時你不得不將眾多類型在不同的系統間進行轉換。這些問題LINQ早就幫你想到了,LINQ會自動為你處理這些轉換,而這些類型系統間的轉換幾乎讓程序員察覺不到。

 更多的訊息
在不同的類型系統和LINQ之間的轉換中存在一切局限性。關于這些信息會在第5章提到,當然,你可以在產品文檔中找到描述更加詳細的類型系統兼容性表。



Language Integration

Language integration is a fundamental aspect of LINQ. The most visible part is the query expression feature, which is present in C# 3.0 and Visual Basic 9.0. It allows you to write code such as the following:

var query =
from c in Customers
where c.Country == "Italy"
orderby c.Name
select new { c.Name, c.City };

instead of writing this code:

var query =
Customers
.Where( c => c.Country == "Italy" );
.OrderBy( c => c.Name )
.Select( c => new { c.Name, c.City } );

Many people call this simplification syntax sugaring because it is just a simpler way to write code that defines a query over data. However, there is more to it than that. Many language constructs and syntaxes are necessary to support what seems to be just a few lines of code that query data. Under the cover of this simple query expression are local type inference, extension methods, lambda expressions, object initialization expressions, and anonymous types. All of these features are useful by themselves, but if you look at the overall picture you should see that there is a big step in two directions: one moving to a more declarative style of coding, and one lowering the impedance mismatch between data and code.

Declarative Programming

What are the differences between an SQL query and an equivalent C# 2.0 or Visual Basic 8.0 program that filters data contained in native storage (such as a table for SQL or an array for C# or Visual Basic)?

In SQL, you can write the following:

SELECT * FROM Customers WHERE Country = 'Italy'

In C#, you would probably write this:

public List<Customer> ItalianCustomers( Customer customers[] )
{
List<Customer> result = new List<Customer>();
foreach( Customer c in customers ) {
if (c.Country == "Italy") result.Add( c );
}
return result;
}

C# code takes longer to write and read. But the most important consideration is expressivity. In SQL, you describe what you want. In C#, you describe how to obtain the expected result. In SQL, the selection of the best algorithm to implement the how (which is more explicitly dealt with in C#) is the responsibility of the query engine. This is because the SQL query engine has more freedom to apply optimizations than a C# compiler, which has many more constraints on the how.


Note 

The previous C# code sample could be written in .NET 2.0 using a Find predicate, but for many developers the required anonymous delegate syntax is not very easy to use. However, the samples are merely illustrative of the different programming paradigms.

LINQ enables a more declarative style of coding for C# and Visual Basic. A LINQ query describes operations on data through a declarative construct instead of an iterative one. LINQ allows the intentions of programmers to be made more explicit, and this knowledge of programmer intent is fundamental to obtaining a higher level of services from the underlying framework. For example, think about parallelization. An SQL query can be split into several concurrent operations, simply because it does not place any constraint on the kind of table scan algorithm applied. A C# foreach loop is harder to split into several loops over different parts of an array that could be executed in parallel by different processors.


More Info 

Parallel LINQ (PLINQ) is a research project on the parallelism that can be obtained by writing code with LINQ.

Declarative programming can take advantage of services offered by compilers and frameworks, and in general it is easier to read and maintain. This single “feature” can be the most important one because it boosts programmers’ productivity. For example, suppose that you want to get a list of all static methods available in the current application domain that return an IEnumerable<T> interface. You can use LINQ to write a query over Reflection:

var query =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
from method in type.GetMethods()
where method.IsStatic
&& method.ReturnType.GetInterface( "IEnumerable`1" ) != null
orderby method.DeclaringType.Name, method.Name
group method by new { Class = method.DeclaringType.Name,
Method = method.Name };

The equivalent C# code that handles data is longer to write, harder to read, and probably more error prone. You can see a possible version that is not particularly optimized in Listing 1-10.

Listing 1-10: C# code equivalent to a LINQ query over Reflection
Image from book

List<String> results = new List<string>();
foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
foreach( var type in assembly.GetTypes() ) {
foreach( var method in type.GetMethods()) {
if (method.IsStatic &&
method.ReturnType.GetInterface("IEnumerable`1") != null) {
string fullName = String.Format( "{0}.{1}",
method.DeclaringType.Name,
method.Name );
if (results.IndexOf( fullName ) < 0) {
results.Add( fullName );
}
}
}
}
}
results.Sort();
Image from book

Type Checking

Another important aspect of language integration is type checking. Whenever data is manipulated by LINQ, no unsafe cast is necessary. The short syntax of a query expression has no compromises with type checking: data is always strongly typed, including both the queried collections and the single entities that are read and returned.

The type checking of the languages that support LINQ (currently C# 3.0 and Visual Basic 9.0) is preserved even when LINQ-specific features are used. This enables the use of Visual Studio features such as IntelliSense and Refactoring, even with LINQ queries. These Visual Studio features are very important for programmers’ productivity.

Transparency Across Different Type Systems

When you think about the type system of the Microsoft .NET Framework and the type system of SQL Server, you realize that they are different. Using LINQ, you give precedence to the .NET type system because it is the one supported by any language that hosts a LINQ query. However, most of your data will be saved in a relational database, and it is necessary to convert many types of data between these two worlds. LINQ handles this conversion for you automatically, making the differences in type systems almost completely transparent to the programmer.


More Info 

Some limitations exist in the capability to perform conversions between different type systems and LINQ. You will find some information about this topic in Chapter 5, and you can find a more detailed type system compatibilities table in the product documentation.