泛型之T運算

2021-05-23 16:16:28 字數 2211 閱讀 1994

泛型技巧系列:避免基類及介面約束

.net泛型的一大特點是在編譯階段對型別引數不做任何假設。也就是說,面對型別引數t和他的變數,你沒有什麼能做的——不能呼叫除object成員之外的任何方法,不能進行大多數運算子的運算等等。它提供了乙個叫約束的機制,能在編譯期對型別實參的取值進行一些檢查。許多人都將約束視為在型別引數上提供操作支援的唯一方法,並大量使用——你有沒有約束過icomparable呢?但是,這種做法是不對的,因為約束僅僅能檢測宣告型別是否實現了某介面或繼承自某類,但通過介面和基類實現的多型機制是乙個執行時檢查的機制,約束沒有從任何方面幫助介面和基類工作。實際上,絕大多數情況下,你用乙個約束了icomparable的型別引數t來程式設計序,和直接用icomparable作為你的操作型別沒有什麼不同。

約束的真正目的是減少型別實參的取值範圍,也就是降低抽象性,是乙個泛型具體化的手法。只有當你清楚,你的目的就是「約束」本身,而不是想給型別引數增加些操作的時候,才應該使用約束。那麼只想給型別引數增加操作應該怎麼辦呢?我推薦的方法是:將實現該操作的功能封裝到乙個外部的輔助類中。比如我們常常想約束icomparable,因為我們想比較型別引數物件的大小。有很多人都用這種方法:

[vb]

sub sort(of t as icomparable(of t))(arr as t())

[c#]

void sort(t arr) where t : icomparable

顯而易見,sort需要陣列元素能夠比較大小,那麼為什麼約束不好呢?注意我們約束的是t實現icomparable(of t),如果t實現的是icomparable呢,就不能比較大小了嗎?約束無法表達「實現了icomparable(of t)或icomparable」這種情況。更進一步,僅有這樣的物件才能比較大小嗎?有許多態別並沒有實現這兩個介面中的任何乙個,但是仍有比較大小的可能,我們一旦約束就等於將他們拒之門外。因此約束是不適合解決此類問題的。記住:我們唯一需要的就是比較,因此正確的方法是這樣:

[vb]

sub sort(of t)(arr as t(), comparer as icomparer(of t))

[c#]

void sort(t arr, icomparercomparer)

注意我們對t現在沒有任何限制,但是要求提供乙個icomparer(of t)的例項。這個icomparer(of t)的實現和t可以沒有關係,因此不僅t可以不知曉它的存在,還可以提供不只一種的比較方法。但是這樣做,無形要求使用者必須提供乙個額外的輔助物件,對於一些顯而易見可比較大小的物件(如int32),這種要求顯得有些多餘。那我們的解決方法就是提供乙個預設的比較器:

[vb]

sub sort(of t)(arr as t())

sort(arr, comparable(of t).default)

end sub

[c#]

void sort(t arr)

**中的comparer(of t)是system.collection.generic下的乙個型別,專門用於提供型別預設的比較器。注意到什麼了嗎?我們現在沒有約束了,但是對使用者來說,和有約束時的語法一樣簡單而清晰。比較那些實現了icomparable和icomparable(of t)的型別時,comparer(of t)都提供了支援,而在比較沒有實現這類介面的自定義型別時,可自行實現icomparer(of t)提供比較機制。完美解決。

這種方法還能進行一些約束都實現不了的做法:支援運算子。我們知道在型別引數上實現哪怕最簡單的加法都是不允許的,而且沒有任何介面可以幫你做到這一點。這時如果能夠使用外部輔助類的做法,就能夠突破這一惱人的限制,比如vbf就用了下面乙個機制來計算型別引數的加法。

[vb]

function add(of t)(a as t, b as t, calc as icalculator(of t))as t

return calc.add(a, b)

end function

function add(of t)(a as t, b as t)as t

return calculator(of t).default.add(a, b)

end function

[c#]

t add(t a, t b, icalculatorcalc)

t add(t a, t b)

現在剩下的問題就是,諸如comparer(of t)和calculator(of t)這類預設的比較器和計算器是如何實現的?如何保證所實現操作的高效率?這就是我們下一次的任務——型別字典和type traits。

泛型之泛型類

public class a 構造引數型別上使用泛型 public a t t 方法返回值上使用泛型 public t gett 方法的引數上使用泛型 這是泛型類的方法,而不是泛型方法 public void sett t t 方法的返回值和引數型別上使用泛型 public t foo t t pu...

泛型之泛型類

public class a 構造引數型別上使用泛型 public a t t 方法返回值上使用泛型 public t gett 方法的引數上使用泛型 這是泛型類的方法,而不是泛型方法 public void sett t t 方法的返回值和引數型別上使用泛型 public t foo t t pu...

泛型之泛型類

public class a 構造引數型別上使用泛型 public a t t 方法返回值上使用泛型 public t gett 方法的引數上使用泛型 這是泛型類的方法,而不是泛型方法 public void sett t t 方法的返回值和引數型別上使用泛型 public t foo t t pu...