之前我在文章通過Roslyn體驗C# 6.0的新語法中介紹了一些C# 6.0的語法特性,現在隨着Visual Studio 14 CTP3的發布,又陸續可以體驗一些新的特性了,這里簡單的介紹一下之前沒有介紹的新語法。
屬性表達式(Property Expressions)
我們常常會在類中寫一些通過函數生成的只讀屬性:
class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public double Distance
{
get { return Math.Sqrt((X * X) + (Y * Y)); }
}
public Point Move(int dx, int dy)
{
return new Point(X + dx, Y + dy);
}
}
現在,可以利用一個Lambda表達式簡化這一過程:
public double Distance => Math.Sqrt((X * X) + (Y * Y));
函數表達式(Method Expressions)
函數表達式和屬性表達式比較類似,使得我們可以通過Lambda表達式簡化成員函數。還是以上面的Point為例,Move函數可以簡化如下
public Point Move(int dx, int dy) => new Point(X + dx, Y + dy);
最后,再總結前文介紹的幾個新特性來一起來簡化Point類:
class Point(int x, int y)
{
public int X { get; set; } = x;
public int Y { get; set; } = y;
public double Distance => Math.Sqrt((X * X) + (Y * Y));
public Point Move(int dx, int dy) => new Point(X + dx, Y + dy);
}
NULL檢查運算符(Monadic null checking)
這個是我非常喜歡的一個語法,例如我們要獲取一個Point序列的第一個點的X坐標,第一感覺會這么寫:
int firstX = points.First().X;
但是,老鳥會告訴你,這兒沒有進行NULL檢查,正確的版本是這樣的:
int? firstX = null;
if (points != null)
{
var first = points.FirstOrDefault();
if (first != null)
firstX = first.X;
}
正確倒是正確了,代碼取變得難讀多了。現在,在C# 6.0中,引入了一個 ?. 的運算符,前面的代碼可以改成如下形式:
int? firstX = points?.FirstOrDefault()?.X;
從這個例子中我們也可以看出它的基本用法:如果對象為NULL,則不進行后面的獲取成員的運算,直接返回NULL
需要注意的是,由於"?."運算符返回的可以是NULL,當返回的成員類型是struct類型的時候,"?."和"."運算符的返回值類型是不一樣的。
Point p = new Point(3, 2);
Console.WriteLine(p.X.GetType() == typeof(int)); //true
Console.WriteLine(p?.X.GetType() == typeof(int?)); //true
另外,除了"?."運算符外,還有一個"?[]"運算符,以使得我們可以寫出如下表達式:
int? first = customers?[0].Orders.Count();
nameof表達式(Nameof expressions)
我們常常在反射或類似的技術中以字符串的形式使用屬性的名稱,拋開拼寫錯誤不談,當我們進行重構而修改屬性名稱的時候,由於字符串類型的屬性得不到編譯器檢查,修改相應的字符串屬性名稱是一件非常令人頭痛的事情,現在有了nameof,再也不用擔心拼錯屬性名稱了。
nameof運算符可以作用於變量、函數、類以及名字空間中,用於返回返回其名稱,例如:
static void Main(string[] args)
{
Console.WriteLine(nameof(Main)); //輸出 "Main"
}
當其參數是由"."運算符拼接起來的時候,只返回最后的名稱,例如:
Console.WriteLine(nameof(ConsoleApplication1.Program)); //輸出 "Program"
這個也可以理解,因為ConsoleApplication1.Program和Program本身就是等價的。
需要注意的是,由於C#允許函數重載,因此是存在同名函數的,例如:
static void foo() { }
static void foo(int x) { }
這樣就存在如下兩個問題:
-
轉到定義應該跳到那個函數?
-
當對其中的某個函數重命名,另一個函數維持原名稱的時候, 使用nameof的地方是否也需要變化?
這兩個問題只是體現在VisualStudio上,並不是語法的歧義,也不影響運行結果。在CodePlex中也有專門的文章討論它,目前的處理方式是:
-
轉到定義應該跳到那個函數? (誰先定義轉到誰)
-
當對其中的某個函數重命名,另一個函數維持原名稱的時候, 使用nameof的地方是否也需要變化? (重命名函數不重命名nameof,其它的類型如屬性等重命名會一起變化)
catch和finally語句塊中支持await
在C# 5.0中引入了await運算符,可以方便我們執行異步運算。當時其並不能在catch和finally中使用,讓人有點意猶未盡的感覺。在C# 6.0放開了這一限制,使用更加方便了。
try
{
res = await Resource.OpenAsync(…);
}
catch (ResourceException e)
{
await Resource.LogAsync(res, e); //現在支持了
}
finally
{
if (res != null) await res.CloseAsync(); //現在也支持了
}
catch支持篩選條件了
catch支持篩選條件也是呼聲比較高的特性之一,現在終於可以省得重新再拋一次了
try
{
foo();
}
catch (Exception e ) if (e.HResult == 0x800000C)
{
//do something
}
其它未支持的特性
我這里只是介紹目前可以使用的新特性,我這里試出來的貌似可以補充的就這么多了。其它還有一些尚未推出的特性等下次有了更新的版本再做介紹。感興趣的朋友可以看看官方的特性實現狀態文檔:http://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Home。目前比較期待的新特性是String interpolation和模式匹配,尤其是模式匹配,希望能早日體驗一下。
另外,目前還沒有發現什么BCL方面的更新介紹,雖然BCL已經比較完善了,但感覺這次更新粒度蠻大的,估計至少會有一些基礎庫的補充的。