5 4 1 在 C 中實現選項型別

2021-08-25 19:57:29 字數 2624 閱讀 1502

5.4.1 在 c# 中實現選項型別

正如我們所看到的,在函式的程式設計中,選項型別是很重要的,我們也希望能夠在 c# 中用函式風格**,因此,需要選項型別的適當 c# 實現。我們已經討論了如何在物件導向的語言中對差別聯合進行編碼,因此,該**的結構類似於我們剛才討論過的計畫型別。在option情況下,我們可以建立有 hasvalue 屬性的單個類(或值型別),有點簡單。然而,我們想要演示在一般情況下,差別聯合的編碼概念,所以,我們將建立乙個基類,option,有 tag 屬性,並為兩個可能的可選值建立的派生類。

提示要使這個型別可重用,我們將把它實現為 c# 的泛型類,option。派生類 some,表示乙個可選值,有 t 型別的值,none 類,表示沒有值的可選值。可以看到清單 5.9 中的源** 。

listing 5.9 generic option type using classes (c#)

enum optiontype ;

abstract class option

public optiontype tag }

} class none: option

} class some: option

private readonly t value;

public t value }

} static class option

public static optionsome(t value)

}泛型基類只包含 tag 屬性,可以有由列舉 optiontype 指定兩個值之一。在兩個派生的類 none和 some的建構函式中設定 tag。第二個派生類攜帶乙個值,因此,它有乙個叫 value 的屬性,是 t型別的。像往常一樣,在函式程式設計中,此屬性是不可變的,所以,它只在建構函式中設定一次。

該**還包括乙個非泛型的工具類,option。我們已經在第三章中實現了相似的類,當時,在 c# 中實現函式式的元組和列表型別。此類的目的是簡化選項值的結構。不直接使用建構函式(new some(10)),我們可以利用 c# 型別推斷,當呼叫泛型方法,寫 option.some(10) 時。

現在,我們如何在 c# 中使用我們的 option?下面的**片斷顯示了來自清單 5.7 **的 c# 版本,嘗試從控制台讀取數字:

optionreadinput()

由於我們使用新的 option類,該方法可以返回單個結果,這可能會,也可能不包含值。在我們看如何可以使用返回值之前,要先新增兩個有用的方法到 option類。在 f# 中,我們使用模式匹配來分別告訴選項;清單 5.10 中的方法讓我們在 c# 中寫類似的**。

listing 5.10 pattern-matching methods for the option class (c#)

public bool matchnone()

public bool matchsome(out t value)

兩個方法都返回乙個布林值,告訴我們這個例項是否表示被測試可選值。第二個還有乙個輸出引數,當這個物件是 some 類的例項時,它被設定為攜帶 option型別的值;否則,輸出引數設定為預設值,即,返回 false。清單 5.11 顯示了我們如何使用readinput 法,使用了兩個工具方法。

listing 5.11 working with the option type (c#)

void testinput() ", parsed);

else if (inp.matchnone())

console.writeline("incorrect input!");

}多虧了 matchsome 和 matchnone 的實用程式,我們不必顯式強制把值轉換繼承的類(例如,some)才能訪問值這個值。然而,它仍缺乏模式匹配的很多有用的功能。編譯器不會驗證我們正在提供**的所有分支。更重要的是,不能寫巢狀的模式,這在 f# 中常見的技巧。例如,你可能要建立選項型別,攜帶乙個元組。這應該寫成簡單的 some(1, "one"),使用 match 構造的模式,可以直接從元組中讀取值:some(num, str)。

差別聯合和物件導向的原則

如果你是有經驗的物件導向的程式設計師,可能注意到,我們剛實現的 option類沒有遵循物件導向的最佳做法。特別,我們使用 tag 屬性作為型別**,有擴充套件方法,而不是使用虛擬方法和多型性。

使用型別**的第乙個原因是教育。在 f# 中,以非常類似的方式,差別聯合被編譯成 .net 的程式集**。當你在 f# 中宣告差別聯合,編譯器將建立單個基類,有型別**,為每種差別聯合情況有乙個派生類。使用種差別聯合的**,比如模式匹配,首先確定哪個派生類,它得到了作為引數值。然後,它可以把這個例項強制轉換為特定的類,訪問種差別聯合情況的引數。

第二個原因是為什麼我們不使用虛擬方法,是這個 option型別,如大多數的差別聯合,不是一種典型的物件導向的類。它設計成不是可擴充套件的,這意味著,我們不指望任何人可以新增新的情況。我們也不必要更改 optiontype 列舉。

如果我們想避免型別**,可以把 matchnone 和 matchsome 實現為虛擬方法。這是可行,因為,這兩個方法顯示有關類層次的完整資訊,就像型別**。在下一章,我們將需要完整的資訊,我們會在其中新增使用選項的幾個方法。這些方法將更多的其他工具,比乙個固有部分的型別,所以,我們會要使用擴充套件方法來實現它們。

現在,你已經看到,在 c# 中如何使用泛型實現選項型別,我們可以把注意力轉回到 f#,顯示了在 f# 庫中如何宣告內建的選項型別。

微軟提議在C 7中為引用增加選項型別

微軟c 語言的專案經理mads torgersen,最近提議在c 7中引入可為選項型別 option types 的引用型別。加入選項型別能讓c 語言變得更安全,正如torgersen所說,在c 裡,因為任一引用型別都能引用乙個空值,從而導致了空引用異常的氾濫成災,而使用選項型別能減少空引用異常的產...

6 4 4 實現選項型別的操作

6.4.4 實現選項型別的操作 繫結 bind 和對映 map 的實現有類似的結構,因為,兩者都是依據選項值進行模式匹配的高階函式。我們來看一看 f 和 c 的實現,這是在 c 中實現函式式概念的最好示例。我們先看一下清單 6.14,這是對映操作的實現。清單 6.14 用 f 和 c 實現 map ...

5 4 4 實現選項型別的操作

5.4.4 實現選項型別的操作 繫結和對映的實現有類似的結構,因為,兩者都是高階函式,模式匹配依據乙個選項值。我們來看一看 f 和 c 的實現,是在 c 中編碼函式概念的好示例。讓我們從清單 6.14 開始,顯示了對映操作的實現。listing 6.14 implementing the map o...