本系列目錄:ASP.NET MVC4入門到精通系列目錄彙總
在學習ASP.NET MVC之前,有必要先了解一下C#3.0所帶來的新的文法特性,這一點尤為重要,因為在MVC項目中我們利用C#3.0的新特性将會大大的提高我們的開發效率,同時,在MVC項目中你将到處可以看到C#3.0新特性的身影。其本質都是“文法糖”,由編譯器在編譯時轉成原始文法。
目錄
- 自動屬性
- 隐式類型 var
- 參數預設值 和 命名參數
- 對象初始化器與集合初始化器
- 匿名類& 匿名方法
- 擴充方法
- 系統内置委托:Func / Action
- Lambda表達式
- 标準查詢運算符(SQO)
- LINQ
這個概念很簡單,其簡化了我們在.NET的時候手寫一堆私有成員+屬性的程式設計方式,我們隻需要使用如下方式聲明一個屬性,編譯器會自動生成所需的成員變量。
回顧傳統屬性概念,屬性的目的:封裝字段,控制 1.讀寫權限 及 2.字段的通路規則(如:年齡範圍)。但平時,主要是用來封裝讀寫權限。
基本用法:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
}
在C#3.0之前,我們是這樣來實作屬性的:
private int id;
public int Id
{
get
{
return id;
}
set
{
id = value;
}
}
思考:
用自動屬性的話程式員寫的代碼少了,機器做的事情就多了,那我們到底要不要使用它?
如果是針對讀寫權限的封裝,就推薦使用,因為它是在編譯的時候産生了負擔,并不是在運作的時候。(不會影響客戶運作程式時的效率!)
但是編譯生成的代碼也有一個顯而易見的缺點,文法太完整,編譯後的程式集會比較大。
隐式推斷類型
這個名稱可能對你很陌生,但是var這個關鍵字應該都用過,在C#中使用var聲明一個對象時,編譯器會自動根據其指派語句推斷這個局部變量的類型。指派以後,這個變量的類型也就确定而不可以再進行更改。另外var關鍵字也用于匿名類的聲明。
應用場合:var主要用途是表示一個LINQ查詢的結果。這個結果可能是ObjectQuery<>或IQueryable<>類型的對象,也可能是一個簡單的實體類型的對象。這時使用var聲明這個對象可以節省很多代碼書寫上的時間。
var customer = new Customer();
var隐式類型的限制
1.被聲明的變量是一個局部變量,而不是靜态或執行個體字段;
2.變量必須在聲明的同時被初始化;編譯器要根據初始化值推斷類型
3.初始化不能是一個匿名函數;
4.初始化表達式不能是 null;
5.語句中隻聲明一次變量,聲明後不能更改類型;
6.指派的資料類型必須是可以在編譯時确定的類型;
public class Dog
{
public void Say(string name = "jf", int age=1)
{
Console.WriteLine(name + "," + age);
}
}
class Program
{
static void Main(string[] args)
{
Dog _dog = new Dog();
_dog.Say();
_dog.Say("haha");
_dog.Say("wuwu", 2);
}
}
運作結果:

如果要name使用預設值,age給值怎麼辦?
_dog.Say(age: 3); //輸入結果:jf,3
對象集合初始化器
在.NET2.0中構造一個對象的方法一是提供一個重載的構造函數,二是用預設的構造函數生成一個對象,然後對其屬性進行指派。在.NET3.5/C#3.0中我們有一種更好的方式來進行對象的初始化。那就是使用對象初始化器。這個特性也是匿名類的一個基礎,是以放在匿名類之前介紹。需要注意的是,它最終還是離不開構造函數,它其實是使用預設的構造函數生成了一個對象。
對象初始化:
User user = new User { Id = 1, Name = "Zouqj", Age = 26 };
集合初始化:
List<Dog> list = new List<Dog>() { new Dog() { Name = "Tom", Age = 1 }, new Dog() { Name = "Lucy", Age = 3 } };
建立并初始化數組:
string[] array = { "aaa", "bbb" };
匿名類
有了前文初始化器的介紹,匿名類就很簡單了。我們可以使用new { object initializer }或new[]{ object, …}來初始化一個匿名類或不确定類型的數組。匿名類的對象需要使用var關鍵字聲明。示例代碼:
var p = new { Id = 1, Name = " Zouqj ", Age = 26 };//屬性名字和順序不同會生成不同類
在編譯後會生成一個【泛型類】,包含:
a. 擷取所有初始值的構造函數,順序與屬性順序一樣;
b.公有的隻讀屬性,屬性不能為null/匿名函數/指針;
c.屬性的私有隻讀字段;
d.重寫的Equals,GetHashCode,ToString()方法
用處:
a.避免過度的資料累積
b.為一種情況特别進行的資料封裝
c.避免進行單調重複的編碼
應用場合:當直接使用select new { object initializer }這樣的文法就是将一個LINQ查詢的結果傳回到一個匿名類中。
注意:
1. 當出現“相同”的匿名類的時候,編譯器隻會建立一個匿名類
2. 編譯器如何區分匿名類是否相同?
根據:屬性名,屬性值(因為這些屬性是根據值來确定類型的),屬性個數,屬性的順序。
3、匿名類的屬性是隻讀的,可放心傳遞,可用線上程間共享資料
匿名方法
函數式程式設計的最大特點之一:把方法作為參數和傳回值。 DGShowMsg -> MulticastDelegate(intPtr[]) -> Delegate(object,intPtr)
匿名方法:編譯後會生成委托對象,生成方法,然後把方法裝入委托對象,最後指派給 聲明的委托變量。
匿名方法可以省略參數:編譯的時候 會自動為這個方法 按照 委托簽名的參數 添加參數
public delegate void ConsoleWrite(string strMsg);
public void WriteMsg(string strMsg)
{
Console.WriteLine("myMsg=" + strMsg);
}
//測試
ConsoleWrite delCW1 = new ConsoleWrite(WriteMsg);
delCW1("哈哈哈哈");
ConsoleWrite delCW2 = delegate(string strMsg) {
Console.WriteLine(strMsg);
};
delCW2("哈哈哈哈");
擴充方法的本質:編譯時,直接将 str.WriteSelf(2015) 替換成 StringUtil.WriteSelf(str,2015);
想為一個類型添加一些成員 , 怎麼辦?
擴充方法:
public static class StringUtil
{
public static void WriteSelf(this string strSelf , int year)
{
Console.WriteLine(strSelf);
}
}
測試:
string str="中國釣魚島";
str.WriteSelf(2015);
本質就是靜态方法
編譯器認為一個表達式是要使用一個執行個體方法,但沒有找到,就會檢查導入的命名空間和目前命名空間裡所有的擴充方法,并比對到适合的方法.
注意:1.執行個體方法優先于擴充方法(允許存在同名執行個體方法和擴充方法)2.可以在空引用上調用擴充方法!3.可以被繼承4.并不是任何方法都能作為擴充方法使用,必須有特征:
它必須放在一個非嵌套、非泛型的靜态類中(的靜态方法);
它至少有一個參數;
第一個參數必須附加 this 關鍵字;
第一個參數不能有任何其他修飾符(out/ref)
第一個參數不能是指針類型
看看這兩個接口的方法:IEnumerable<T> ,IQueryable<T>
委托使用可變性 :
Action<object> test=delegate(object o){Console.WriteLine(o);};
Action<string> test2=test;
Func<string> fest=delegate(){return Console.ReadLine();};
fest2=fest;
public delegate void Action();
public delegate bool Predicate<in T>(T obj);
public delegate int Comparison<in T>(T x, T y);
協變指的是委托方法的傳回值類型直接或間接繼承自委托簽名的傳回值類型,逆變則是參數類型繼承自委托方法的參數類型
System.Func 代表有傳回類型的委托
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
......
注:輸入泛型參數-in 最多16個,輸出泛型參數 -out 隻有一個。
System.Action 代表無傳回類型的委托
public delegate void Action<in T>(T obj); //list.Foreach
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
注:參數最多16個
System.Predicate<T> 代表傳回bool類型的委托 - 用作執行表達式
public delegate bool Predicate<in T>(T obj); //list.Find
System.Comparison<T> 代表傳回int類型的委托 - 用作比較兩個參數的大小
public delegate int Comparison<in T>(T x, T y); //list.Sort
為什麼要定義這麼多簡單的委托? 友善!
Lambda表達式的本質就是匿名函數,Lambda表達式基于數學中的λ演算得名,直接對應于其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。“Lambda 表達式”是一個匿名函數,它可以包含表達式和語句,并且可用于建立委托或表達式樹類型。
Lambda 表達式的運算符 =>,該運算符讀為“goes to”。
=> 運算符具有與指派運算符 (=) 相同的優先級
Lambda的基本形式:(input parameters) => expression
隻有在 Lambda 有一個輸入參數時,括号才是可選的;否則括号是必需的。 兩個或更多輸入參數由括在括号中的逗号分隔: (x, y) => x == y
有時,編譯器難于或無法推斷輸入類型。 如果出現這種情況,您可以按以下示例中所示方式顯式指定類型: (int x, string s) => s.Length > x
使用空括号指定零個輸入參數: () => SomeMethod()
最常用的場景:Ienumable和Iqueryable接口的Where<>(c=>c.Id>3)
下列規則适用于 Lambda 表達式中的變量範圍:
捕獲的變量将不會被作為垃圾回收,直至引用變量的委托超出範圍為止。
在外部方法中看不到 Lambda 表達式内引入的變量。
Lambda 表達式無法從封閉方法中直接捕獲 ref 或 out 參數。
Lambda 表達式中的傳回語句不會導緻封閉方法傳回。
Lambda 表達式不能包含其目标位于所包含匿名函數主體外部或内部的 goto 語句、break 語句或 continue 語句。
Lambda表達式縮寫推演
例子:
delegate int AddDel(int a,int b); //定義一個委托
#region lambda
AddDel fun = delegate(int a, int b) { return a + b; }; //匿名函數
//Console.WriteLine(fun(1, 3));
//lambda 參數類型可以進行隐式推斷,可以省略類型 lambda本質就是匿名函數
AddDel funLambda = ( a, b) => a + b;
List<string> strs = new List<string>() { "1","2","3"
};
var temp = strs.FindAll(s => int.Parse(s) > 1);
foreach (var item in temp)
{
Console.WriteLine(item);
}
//Console.WriteLine(funLambda(1,3));
#endregion
static void Main(string[] args)
{
List<int> nums = new List<int>() { 1,2,3,4,6,9,12};
//使用委托的方式
List<int> evenNums = nums.FindAll(new Program().GetEvenNum);
foreach (var item in evenNums)
{
Console.WriteLine(item);
}
Console.WriteLine("使用lambda的方式");
List<int> evenNumLamdas = nums.FindAll(n => n % 2 == 0);
foreach (var item in evenNumLamdas)
{
Console.WriteLine(item);
}
Console.ReadKey();
//nums.FindAll(
}
public bool GetEvenNum(int num)
{
if (num % 2 == 0)
{
return true;
}
return false;
}
标準查詢運算符:定義在System.Linq.Enumerable類中的50多個為IEnumerable<T>準備的擴充方法,這些方法用來對它操作的集合進行查詢篩選.
- 篩選集合Where:需要提供一個帶bool傳回值的“篩選器”,進而表明集合中某個元素是否應該被傳回。
- 查詢投射,傳回新對象集合 IEnumerable<TSource> Select()
- 統計數量int Count()
- 多條件排序 OrderBy().ThenBy().ThenBy()
- 集合連接配接 Join()
延遲加載:Where
即時加載:FindAll
SQO缺點:語句太龐大複雜
C#3.0新文法:查詢表達式,和SQL風格接近的代碼
IEnumerable<Dog> list = from dog in dogs
where dog.Age>5
//let d=new{Name=dog.Name}
orderby dog.Age descending
select dog;
//select new{Name=dog.Name}
以"from"開始,以"select 或 group by子句"結尾。輸出是一個 IEnumerable<T> 或 IQueryable<T> 集合;
注:T 的類型 由 select 或 group by 推斷出來。
LINQ分組:
IEnumerable<IGrouping<int, Dog>> listGroup = from dog in listDogs where dog.Age > 5 group dog by dog.Age;
周遊分組:
foreach (IGrouping<int, Dog> group in listGroup)
{
Console.WriteLine(group.Key+"歲數:");
foreach (Dog d in group)
{
Console.WriteLine(d.Name + ",age=" + d.Age);
}
}
最後:LINQ 查詢語句 編譯後會轉成 标準查詢運算符
此外,我建議大家多使用reflector工具來檢視C#源碼和IL語言,reflector就像一面照妖鏡,任何C#文法糖在它面前都将原形畢露。
部落格位址: | http://www.cnblogs.com/jiekzou/ |
部落格版權: | 本文以學習、研究和分享為主,歡迎轉載,但必須在文章頁面明顯位置給出原文連接配接。 如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起讨論,共同進步! 再次感謝您耐心的讀完本篇文章。 |
其它: | .net-QQ群4:612347965 java-QQ群:805741535 H5-QQ群:773766020 我的拙作《ASP.NET MVC企業級實戰》《H5+移動應用實戰開發》 《Vue.js 2.x實踐指南》 《JavaScript實用教程 》 《Node+MongoDB+React 項目實戰開發》 已經出版,希望大家多多支援! |