天天看點

編寫高品質代碼改善C#程式的157個建議[協變和逆變]

前言

  建議42、使用泛型參數相容泛型接口的不可變性

  建議43、讓接口中的泛型參數支援協變

  建議44、了解委托中的協變

  建議45、為泛型類型參數指定協變

建議42、使用泛型參數相容泛型接口的不可變性

讓傳回值類型傳回比聲明的類型派生程度更大的類型,就是“協變”。協變不是一種新出現的技術,在以往的編碼中,我們已經在不自覺地使用協變。以下的代碼就是一個不自覺應用協變的例子:

Programmer是Employee的子類,是以一個Programmer對象也就是一個Employee對象。方法GetAEmployee傳回一個Programmer的對象,也就是相當于傳回了一個Employee對象。正是因為在FCL4.0以前的版本中,協變是如此自然的一種應用,是以我們很有可能寫出這樣的代碼:

接下來看一下Main函數中的調用:

咋眼一看,問題應該不大,不過編譯器卻出現了錯誤。

編寫高品質代碼改善C#程式的157個建議[協變和逆變]

編譯器對于接口和委托類型參數的檢查是非常嚴格的,除非用關鍵字out特别聲明(這個會在下一個建議中進行闡述),不然這段代碼隻會編譯失敗,要讓PrintSalary完成需求,我們可以使用泛型類型參數:

可能有人會注意到,本建議開頭處指出“協變”是針對傳回值而言的,但是所舉的這個例子卻并沒有展現“傳回值”這個概念。實際上,隻要泛型類型參數在一個接口聲明中不被用來作為方法的輸入參數,我們都可姑且把它堪稱是“傳回值”類型的。是以,本建議中這種模式是滿足“協變”的定義的。但是,隻要将T作為輸入參數,便不滿足“協變”的定義了。
編寫高品質代碼改善C#程式的157個建議[協變和逆變]

建議43、讓接口中的泛型參數支援協變

 除了建議42中提到的使用泛型參數相容泛型接口的不可變性外,還有一種辦法就是為接口中的泛型聲明加上out關鍵字來支援協變,如下所示:

out關鍵字是FCL4.0中新增的功能,它可以在泛型接口和委托中使用,用來讓類型參數支援協變性。通過協變,可以使用比聲明的參數派生類型更大的參數。通過以上的例子我們應該能了解這種應用。

FCL4.0對多個接口進行了修改以支援協變,如IEnumerable<out T>、IEnumerator<out T>、IQueryable<out T>等。由于IEnumerable<out T>現在支援協變,是以上段代碼在FCL4.0中能運作得很好。

在我們自己的代碼中,如果要編寫泛型接口,除非确定該接口中的泛型參數不涉及變體,否則都建議加上out關鍵字。協變增大了接口的使用範圍,而且幾乎不會帶來什麼副作用。

建議44、了解委托中的協變

委托中的泛型變量天然是部分支援協變的。為什麼說是“部分支援協變”呢?來看一下下面的例子:

上文中,方法GetAManager傳回的是一個Manager,但是在使用中,其實是将其指派給了一個泛型參數為Employee的委托變量。我們也許會認為委托中的泛型變量不再需要out關鍵字,這是錯誤的了解。因為存在下面這樣一種情況,是以編譯報錯:

編寫高品質代碼改善C#程式的157個建議[協變和逆變]

要讓上面的代碼編譯通過,同樣需要為委托中的泛型參數指定out關鍵字:

除非考慮到該委托聲明肯定不會用于可變性,否則,為委托中的泛型參數指定out關鍵字将會拓展該委托的應用,建議在實際的編碼工作中永遠這樣使用,實際上,FCL4.0中的一些委托聲明已經用out關鍵字來讓委托支援協變了,如我們常常會使用到的:

建議45、為泛型類型參數指定協變

 逆變是指方法的參數可以是委托或泛型接口的參數類型的基類。FCL4.0中支援逆變的常用委托有:

常用泛型接口有:

下面的例子示範了泛型類型參數指定逆變所帶來的好處:

在上面的這個例子中,如果不為接口IMyComparable的泛型參數T指定in關鍵字,将會導緻Test(p,m)編譯錯誤。由于引入了接口的逆變性,這讓方法Test支援了更多的應用場景。在FCL4.0之後版本的實際編碼中應該始終注意這一點。

英語小貼士

1、Then, please give me a new reservation.——那麼,請幫我重新訂位。

2、Sorry, this flight is full.——抱歉,這班飛機已客滿。

3、What is the possibility of my gettinga seat if I wait?——若是我在此等候,有機位的機率有多大?

4、When will the next flight to Los Angeles leave?——下一班飛往洛杉機的班機何時起飛?

5、The day after tomorrow, Friday.——後天,星期五。

6、That will be fine. What's the flight number and departure time?——太好了。請告訴我班機号碼與起飛時間?

7、What is the fare?——費用多少?

感謝您的閱讀,如果您對我的部落格所講述的内容有興趣,那不妨點個推薦吧,謝謝支援:-O。