帶你領略C#7 特性程式碼範例

黄舟
發布: 2017-03-09 15:36:21
原創
1383 人瀏覽過

帶你來領略C#7 特性程式碼範例:

#

# 元組值類型

# .NET提供了一個元組(Tuple)類型,但具體在C#中使用時卻存在著各種各樣的問題。由於元組類型是一個引用類型,因此在一些對於效能相當敏感的程式碼中,你很可能會避免因使用它而造成GC的開銷。同時,元組類型是不可變的,雖然這使跨線程共享變得更安全,但也意味著每次進行變更都必須分配一個新的物件。

為了因應這個問題,C# 7將提供一個值類型的元組。這是一個可變類型,對那些重視效能的程式碼來說,這種方式將更有效率。同時,作為值類型,它在每次進行分配時都會產生一個拷貝,因此幾乎沒有產生多執行緒問題的風險。

你可以透過以下語法建立一個元組:

var result = (5, 20);
登入後複製

你也可以選擇對元組中的值進行命名,這並不是必須的,只是讓程式碼有更好的可讀性。

var result = (count: 5, sum: 20);
登入後複製

你可能會想,「很棒的特性,但我自己也能寫出來」。但下一個特性才是重頭戲。

多傳回值

# 在類別C風格的語言中,要在一個函數中傳回兩個值始終是一件麻煩事。你只能選擇將結果封裝成某種結構,或是使用輸出參數。就像許多函數式程式語言一樣,C#選擇了第一種方式為你提供這個特性:

(int, int) Tally (IEnumerable<int> list)
登入後複製

可以看到,在這裡使用泛用的元組有一個基本問題:我們將無從得知每個字段的作用。因此,C#選擇透過一個編譯器花招對結果進行命名:

(int Count, int Sum) Tally (IEnumerable<int> list)
登入後複製

我們在此需要強調一點:C#並沒有產生一個新的匿名類型,你所得到的仍舊是一個元組,但編譯器將假設它的屬性為Count和Sum,而不是Item1和Item2。所以,以下程式碼行的作用都是等價的:

var result = Tally(list);
Console.WriteLine(result.Item1);
Console.WriteLine(result.Count);
登入後複製

請注意一點,我們現在還不具備多賦值語法,如果這種語法最終實現,那麼它的用法可能是這樣的:

(count, sum) = Tally(list);
登入後複製

除了提供簡單的功能性函數之外,多重傳回值的實用性也體現在非同步程式碼的編寫上,因為在async函數中是不允許使用out參數的。

模式匹配:改進的Switch語法區塊

VB與函數式程式設計師對於C#抱怨得最多的一點就是C#中的switch語句功能十分有限。 VB開發者希望能夠進行範圍匹配,而習慣了F#或Haskell的開發者則希望能夠使用分解式的模式匹配。 C#打算同時提供這兩種特性。

在對類型進行模式配對時,你可以建立一個變數以保存轉型的結果。舉例來說,在對一個System.Object使用switch語句時,你可以寫以下程式碼:

case int x:
登入後複製

如果該物件是數值類型,則變數x將得以賦值。否則的話,程式將按從上至下的順序檢查下一個case語句區塊。如果你想更具體地進行匹配,也可以使用範圍檢查:

case int x when x > 0:
case int y:
登入後複製

# 在這個範例中,如果該物件是正整數,則x程式碼區塊將被執行。如果物件是0或負整數,而y程式碼區塊將被執行。

如果需要檢查null值,則只需使用以下語法:

case null;
登入後複製

模式匹配:分解

# 到目前為止,我們僅僅展示了某種對VB中已有的特性所做的增量式改進,而模式匹配真正的強大之處在於分解,它可以將某個物件完全拆開,考慮一下以下語法:

if (person is Professor {Subject is var s, FirstName is "Scott"})
登入後複製

這段程式碼完成了兩件事:

  1. 它創建了一個本地變數s,將其賦值為((Professor)person).Subject。

  2. # 它執行了一次相等性檢查 ((Professor)person).FirstName == "Scott"。

如果將其以C# 6程式碼改寫則是這樣:

var temp = person as Professor;
if (temp != null && temp.FirstName == "Scott")
{
    var s = temp.Subject
登入後複製

# 在最終發布中,我們預計能夠同時看到對switch語句塊的這兩種改進。

引用返回

# 對於大數據結構進行引用傳遞比起值傳遞快得多,因為後者需要對整個結構進行拷貝。與之類似,返回一個大數據結構的引用一樣能夠提升速度。

在類似C這樣的語言中,可以透過指標傳回某個結構的引用。這種方式會帶來一個常見的問題,即指標所指向的記憶體可能會因為某種原因而已經被回收了。

C#透過使用引用的方式來迴避這個問題,引用本身就是一個附加了規則的指標。最重要的一條規則是,你不能夠回傳某個本地變數的參考。如果你嘗試這樣做,那麼該變數所引用的棧資訊在函數返回時就已經變得不可訪問了。

在微软的展示代码中,它所返回的引用指向一个数组中的某个结构。由于它实质上是指向数组中某个元素的指针,因此随后可以对数组本身进行修改。举例来说:

var x = ref FirstElement(myArray)
x = 5; //MyArray[0] now equals 5
登入後複製

这一语法的用例是对性能高度敏感的代码,在大多数应用中都无需使用这一特性。

二进制字面值(Binary Literals)

此次发布还引入了一个小特性,即二进制字面值。这一语法只是一个简单的前缀而已,例如5可以表示为“0b0101”。这一特性的主要用例是设置基于flag的枚举,以及创建位掩码(bitmask),以用于与C风格语言的互操作。

本地函数

本地函数是指在另一个函数中所定义的函数。第一眼看来,本地函数似乎只是比匿名函数稍好的一种语法。但它实际上还存在几个优点:

  • 首先,你无需为其分配一个委托以保存该函数。这不仅减少了内存压力,同时还允许编译器对该函数进行内联操作。

  • 其次,在创建闭包时,也无需为其分配一个对象,因为它能够直接访问本地变量。这一点同样能够改善性能,因为它也减少了GC的压力。

按照第二条规则推算,你将无法创建一个指向本地函数的委托。这一点对于代码的组织其实是一个优点,因为你无需创建独立的函数,并且将现有函数的状态作为显式的参数进行传递。

部分类的改进

最后演示的特性是一种处理部分类的新方式。在过去,部分类的应用是基于代码生成优先的概念而出现的。所生成的代码将包含一系列部分方法,开发者可以选择实现这些方法,以调整类的行为。

通过新的“replace”语法,开发者就多了一种新选择,能够以最直接的方式编写代码,随后再引入代码生成器,并重写这些方法。以下将通过一个简单的示例表现开发者的代码编写方式:

public string FirstName {get; set;}
登入後複製

简单又清晰,但完全不符合XAML风格应用的写法。因此,代码生成器将生成如下代码:

private string m_FirstName;
static readonly PropertyChangedEventArgs s_FirstName_EventArgs =new PropertyChangedEventArgs("FirstName")

replace public string FirstName {
    get {
        return m_FirstName;
    }
    set {
        if (m_FirstName == value)
            return;
    m_FirstName = value;
    PropertyChanged?.Invoke(this, m_FirstName_EventArg);
}
登入後複製

通过“replace”关键字,所生成的代码将直接替换手写的代码,添加所缺失的功能。在这个示例中,我们甚至还能够处理一些开发者经常会忽略的麻烦的部分,例如对EventArgs对象进行缓存。

虽然这个官方示例仅用于属性变更通知,但这一技术还可用于各种“面向切面编程(AOP)”的场景,例如在代码中注入日志记录、安全检查、参数校验以及其他各种繁琐的样板式代码。

如果读者想实际了解一下这些特性,可以观赏Channel 9中的视频“The Future of C#”。


以上是帶你領略C#7 特性程式碼範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!