【LINQ技術】擴充套件特性和LINQ操作符
LINQ特有的程式設計結構
LINQ就像是嵌入到C#中的強型別查詢語言,儘管和SQL查詢很像,但語法卻並不相同,甚至還有截然相反的一面。
LINQ是在.NET發展到3.5版的時候被引進的,C#和VB語言都為此做了許多工作,擴充套件了大量新的程式設計結構。
一、隱式型別本地變數
var——一個如此小巧的關鍵字卻有著強大的力量。
var varInt=1;
var varBool=True;
var varString="String, String, String";
Console.WriteLine("varInt is a: {0}",varInt.GetType().Name);
Console.WriteLine("varBool is a: {0}",varBool.GetType().Name);
Console.WriteLine("varString is a: {0}",varString.GetType().Name);
上面的程式碼會很神奇的自動顯示出它們各自的型別。
var的限制:
1.var不能用於欄位資料
2.var不能用於返回值或引數型別
3.必須在宣告時分配值,且值不為NULL
但var可以這樣:
var alarmClock=new AlarmClock();
alarmClock=null;
var varInt=1;
var varInt2=varInt;
bool varBool=True;
var varBool2=varBool;
static int Alarm()
{
var alarm="09:20";
return alarm;
}
隱式型別資料是強型別資料。型別推斷延續了C#語言的強型別特性,並且只會在編譯時影響變數的宣告。之後,該資料點被視為它宣告的型別。為該變數分配不同的型別將導致編譯時錯誤。
// 編譯器知道“s”是一個string型別
var s="This is a string.";
s="Funny...";
// 因此可以呼叫string的所有成員
string bigS=s.ToUpper();
// 但不能將非string型別的資料分配給s
s=True;
var為LINQ而生
LINQ技術使用的是查詢表示式,它可以根據表示式本身的格式產生動態建立的結果集。但有時在某些情況下根本無法顯示定義查詢的訪問型別,這時隱式型別就會發揮作用了。
二、物件和集合初始化語法
在擴充套件這個新的特性之前,我們要建立一個物件並給其屬性初始化會是這樣:
var rect = new Rect();
rect.Height=100;
rect.Width=200;
但是支援C# 3.0加入了這個新特性,程式碼就成了這樣:
var rect = new Rect(){Height=100,Width=200};
如果有建構函式的話還可以在括號內傳入引數呢,就像這樣:
var rect = new Rect("bigRect"){Height=100,Width=200};
如果對於集合,那就優勢就更加明顯了。
List<Alarm> alarmList = new List<Alarm>
{
new Alarm { Name = "todayAlarm", Description=new Desc{ Time="07:20",Location="Home"}},
new Alarm { Name = "tomorrowAlarm", Description=new Desc{
Time="08:40",Location="Company"}},
new Alarm { Name = "nextYearAlarm", Description=new Desc{
Time="18:40",Location="Shanghai"}},
};
將物件/集合初始化語法和隱式型別本地變數相結合就可以宣告匿名型別。
三、匿名型別
通過這個特性可以快速建立資料的“結構”,編譯器將根據名稱/值對的集合在編譯時生成的類。該型別是基於值的語義構建的,因此System.Object中的每個虛方法都要重寫。要定義一個匿名型別,可以宣告一個隱式型別變數,並使用物件初始化語法指定資料的結構。
var alarm=new
{
currentTime=DateTime.Now,
alarm=new {Name="todayAlarm",Location="Shanghai",Time="18:20"},
};
四、擴充套件方法
通過物件導向的繼承機制,我們可以給一個類新增新的方法等,但這不是唯一的方法。
C#的擴充套件方法不用子類就能向已知型別中新增新的功能,當然了,它還可以向不能有子類的密封類和結構中新增新的功能。但是需要注意的是:
1.在寫擴充套件方法時,第一個引數必須使用this限定符,用來表示被擴充套件的型別
2.第一個引數不能有ref或者out的修飾符
3.第一引數還不能是指標型別
2.擴充套件方法只能存在於靜態類中,並且必須使用static關鍵字將方法宣告為靜態的
而且使用擴充套件方法並不會影響效能,因為這些都是編譯器需要做的,而通過繼承則需要影響效能。
五、Lambda表示式
Lambda表示式可算是Lisp語言的核心了,如果想要了解該語言可以訪問我的其他部落格。C#新增了這個特性可謂是有了質的提升。
Lambda大大簡化了.NET委託的使用,減少了需要手工輸入的程式碼。
List<int> list=new List<int>();
list.AddRange(new int[]{10,21,4,8,3,59});
List<int> list2=list.FindAll(i=>(i%2)==1);
Console.WriteLine("Here are your odd numbers:");
foreach(int n in list2)
{
Console.Write("{0}\t",n);
}
C# LINQ查詢操作符是呼叫System.Linq.Enumerable類中方法的簡便方式,這些方法通常都使用委託作為引數,用來處理資料生成正確的結果集。
LINQ的用途
1.資料
作為軟體開發者,程式設計的絕大部分時間中都在操作著資料。資料有哪些來源?我們可能會從使用者輸入中得到資料,也可能從配置檔案中得到資料,還可能從網路中得到資料,甚至可能從WCF服務返回的記憶體中得到資料。但是在操作特定的資料時,我們往往會使用不同的API。
資料 | 運算元據的方式 |
---|---|
關係資料 | System.Data.dll和System.Data.SqlClient.dll等 |
XML文件資料 | System.Xml.dll |
後設資料表 | System.Reflection名稱空間 |
物件集合 | System.Array和System.Collections/System.Collections.Generic |
我們可以使用ADO.NET、XML名稱空間、Reflection(反射)服務還有各種對於集合的操作。但就這些API本身而言,它們都是獨立的個體。而LINQ API則傾向於提供一個同一且對稱的方式,以便我們能夠在廣義的資料上得到和操作“資料”。通過使用LINQ,我們便可以直接建立被稱為查詢表示式(query expression)的實體。這些查詢表示式是基於許多查詢操作符(query operator)的,而且有意設計為類似SQL表示式。
而根據LINQ查詢的應用場景,可以分為以下5個部分:
LINQ to Object: 針對陣列和集合使用的LINQ查詢
LINQ to XML: 使用LINQ來操縱和查詢XML文件
LINQ to DataSet: 針對ADO.NET DataSet物件使用的LINQ查詢
LINQ to Entity: 對ADO.NET Entity Framework (EF)API使用的LINQ查詢
Parallel LINQ (PLINQ): 並行處理LINQ查詢返回的結果
2.LINQ表示式是強型別的
和傳統的SQL語句不同,LINQ查詢表示式是強型別的,所以我們必須保證這些表示式在語法上都是合理的。因此我們要充分利用Visual Studio這個IDE的智慧感知、自動感知等有用的功能。
LINQ運算元組
通過下面這個示例,我們可以將陣列中的int數值取出奇數並排序。
static void Main(string[] args)
{
LINQDemo();
}
static void LINQDemo()
{
int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
IEnumerable<int> subset = from a in array
where a % 2 == 1
orderby a
select a;
foreach(int i in subset)
{
Console.Write("Item: {0}\n", i);
}
}
最後獲得的結果的集合是由一個實現了IEnumerable< T >泛型版本的物件來表示的。前面我們介紹了var,這裡可以用嗎?當然可以。
var subset = from a in array
where a % 2 == 1
orderby a
select a;
如果使用var的話,那麼在foreach中也需要將int改為var。一般來說,最好在獲取LINQ查詢結果時都使用隱式型別,但在絕大多是情況下,真正的返回值是實現了IEnumerable< T >介面的型別。
延遲執行和立即執行
在迭代內容之前,LINQ查詢表示式並不會真正進行計算。這就叫“延遲執行”,它能夠讓相同的容器執行多次相同的LINQ查詢,而始終獲得最新的結果。
static void LINQDemo()
{
int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
var subset = from a in array
where a % 2 == 1
orderby a
select a;
foreach(var i in subset)
{
Console.Write("Item: {0}\n", i);
}
Console.WriteLine();
array[0]=71;
foreach(var i in subset)
{
Console.Write("Item: {0}\n", i);
}
Console.WriteLine();
}
這樣一來在第二次的輸出中就會在陣列的頭部新增一個71。
這裡就是在foreach中運算的LINQ表示式,但如果希望在foreach之前就運算呢?可以呼叫Enumerable型別定義的許多擴充套件方法。它定義了ToArray< T >()、ToList< T >()和ToDictionary
static void LINQDemo()
{
int[] array = { 8, 30, 13, 35, 89, 31, 83, 58, 32, 76 };
int[] subsetInt = (from a in array
where a % 2 == 1
orderby a
select a).ToArray<int>();
List<int> subsetList = (from a in array
where a % 2 == 1
orderby a
select a).ToList<int>();
}
記得將整個LINQ表示式用圓括號括起來,這樣就能將它強制轉換為正確的實際型別來呼叫Enumerable的擴充套件方法。
C#編譯器不得不說真的很強大,它能夠準確的檢測泛型項的型別引數,我們不需要指定型別引數。因此也可以像下面這樣:
int[] subsetInt = (from a in array
where a % 2 == 1
orderby a
select a).ToArray();
立即執行的好處會體現在當要對外部呼叫者返回LINQ查詢時。
LINQ 查詢操作符
查詢操作符 | 含義 |
---|---|
from、in | 用於定義任何LINQ表示式的主幹,允許從合適的容器中提取資料子集 |
where | 用於定義從一個容器裡取出哪些項的限制條件 |
select | 用於從容器中選擇一個序列 |
join、on、equals、into | 基於指定的鍵來做關聯操作,但這些“關聯”不必與關聯式資料庫的資料有什麼關係 |
orderby、ascending、descending | 允許結果子集按升序或降序排序 |
group、by | 用特定的值來對資料分組後得到一個子集 |
1.獲取資料子集
使用where操作符可以從資料容器裡得到特定的子集,where後應該是運算結果為布林值的表示式。當然了,對於&& 和 || 這些操作也都是可以得。
2.投影新的資料型別
如果想要傳入的Alarm[]型別的alarm集合中得到一個只有時間和名字的結果集,可以定義一個select語句,動態生成一個新的匿名型別。
static void GetNameAndTime(Alarm[] alarm)
{
var alarmSubset=from a in alarm select new {a.Name,a.Time};
foreach (var i in alarmSubset)
{
Console.WriteLine(i.ToString());
}
}
因為在使用投影的LINQ查詢時,我們無法知道實際的資料型別,因為它是在編譯時決定的,所以就必須使用var關鍵字。因此也就無法在建立方法時返回隱式型別。
但如果需要返回這些資料呢,難道就沒有辦法嗎?可以用前面介紹的ToArray()擴充套件方法將查詢結果轉換為.NET System.Array物件。
static Array GetAlarmProp(Alarm[] alarm)
{
var alarmSubset=from a in alarm select new {a.Name,a.Time};
return alarmSubset.ToArray();
}
最後我們可以在Main()中如下呼叫和處理資料:
Array al=GetAlarmProp(alarm);
foreach( object o in al)
{
Console.WriteLine(0);
}
3.獲取資料集中的個數
在投影一批新的資料時,比如在1000個Computer中獲取所有的Windows作業系統的,那麼如果知道它們的總數呢?
int pc=(from c in computer where c.operation="Windows" select c).Count();
4.反轉結果集
想要將最終的結果進行反轉,可以通過Enumerable類中的擴充套件方法Reverse< T >()對結果集中的項進行反轉。
static void GetNameAndTime(Alarm[] alarm)
{
var alarmSubset=from a in alarm select new {a.Name,a.Time};
foreach (var i in alarmSubset.Reverse())
{
Console.WriteLine(i.ToString());
}
}
5.對錶達式進行排序
在前面我們曾使用orderby對獲取到的奇數進行排序,那麼為什麼直接用“orderby a”就可以排序呢?很簡單,因為預設是正序……對於字串是按字母順序,對數字就是從小到大。如果要使用逆序可以使用“orderby a descending”,正序是預設的,不過也可以加上ascending。
6.維恩圖工具
Enumerable類提供了一些擴充套件方法,可以對兩個(或多個)LINQ查詢的資料進行合併(union)、比較(difference)、連線(concatenation)和交叉(intersection)。比如:
List<string> myMoney=new List<string>{"1元","2元","5元","10元"};
List<string> yourMoney=new List<string>{"10元","20元","50元","100元"};
var ourMoney=(from m in myMoney select m).Union(from y in yourMoney select y);
這樣一來,我們的前就合併了哈……
7.移除重複
移除重複就像前面的反轉一樣:
foreach (var i in alarmSubset.Reverse())
{
Console.WriteLine(i.ToString());
}
foreach (var i in alarmSubset.Distinct())
{
Console.WriteLine(i.ToString());
}
8.聚合操作
前面的Count()就是聚合中的一個例子,此外還有Average()、Max()、Min()、Sum()等等。
感謝您的訪問,希望對您有所幫助。
歡迎大家關注或收藏、評論或點贊。
為使本文得到斧正和提問,轉載請註明出處:
http://blog.csdn.net/nomasp
相關文章
- LINQ擴充套件方法套件
- LINQ系列:Linq to Object投影操作符Object
- LINQ系列:Linq to Object限制操作符Object
- LINQ系列:Linq to Object排序操作符Object排序
- LINQ系列:Linq to Object聚合操作符Object
- LINQ系列:Linq to Object集合操作符Object
- LINQ系列:Linq to Object生成操作符Object
- LINQ系列:Linq to Object元素操作符Object
- LINQ系列:Linq to Object相等操作符Object
- LINQ系列:Linq to Object聯接操作符Object
- LINQ系列:Linq to Object分組操作符Object
- LINQ系列:Linq to Object串聯操作符Object
- LINQ系列:Linq to Object轉換操作符Object
- LINQ系列:Linq to Object量詞操作符Object
- LINQ系列:Linq to Object分割槽操作符Object
- Linq查詢語法與擴充方法
- Linq 下的擴充套件方法太少了,您期待的 MoreLinq 來啦套件
- LINQ系列:C#中與LINQ相關特性C#
- [C#3.0體驗]Orcas中內建的LinQ,XLinQ[DLinQ]擴充套件方法C#套件
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- Lynx技術分析-JS引擎擴充套件技術基礎JS套件
- LINQ系列:LINQ to XML操作XML
- 第3章 LALR等技術擴充套件套件
- C#新特性:匿名類和擴充套件方法C#套件
- LINQ系列:LINQ to XML查詢XML
- LINQ系列:LINQ to SQL Take/SkipSQL
- Java 缺失的特性:擴充套件方法Java套件
- Linq
- Lynx技術分析-JS引擎擴充套件設計JS套件
- 與LINQ有關的語言特性
- 對於Linq查詢關鍵字及await,async非同步關鍵字的擴充套件使用AI非同步套件
- LINQ系列:LINQ to DataSet的DataTable操作
- LINQ系列:LINQ to DataSet的DataView操作View
- LINQ系列:LINQ to SQL Where條件SQL
- LINQ系列:LINQ to SQL Concat/UnionSQL
- LINQ系列:LINQ to SQL Join連線SQL
- .Net3.5新特性-擴充套件方法套件
- LINQ系列:LINQ to SQL Select查詢SQL