本篇純屬擡槓之做,以前咱們提到了Swift的泛型Protocol使用associatedtype關鍵字,而不是使用<Type>語法的泛型參數。這其中有什麼好處呢?git
我就這個問題搜索了一些回答,大致上提到兩點:github
<Type>語法對Protocol沒有意義,Protocol僅須要定義一個抽象的概念,具體的類型應該由實現的Class來明確,好比:swift
ClassWithInt<Int>: NumberProtocol ClassWithDouble<Double>: NumberProtocol
associatedtype能夠用來給Protocol中特定Func添加泛型約束,而不是限定整個Protocol函數
protocol GeneratorType { associatedtype Element public mutating func next() -> Self.Element? }
聽上去仍是有必定道理的,而後實踐是檢驗事實的惟一標準。下面咱們經過代碼實例來和C#進行對比。首先拿出網上多被引用解釋上述兩個觀點的Swift代碼:this
public protocol Automobile { associatedtype FuelType associatedtype ExhaustType func drive(fuel: FuelType) -> ExhaustType } public protocol Fuel { associatedtype ExhaustType func consume() -> ExhaustType } public protocol Exhaust { init() func emit() } public struct UnleadedGasoline<E: Exhaust>: Fuel { public func consume() -> E { print("...consuming unleaded gas...") return E() } } public struct CleanExhaust: Exhaust { public init() {} public func emit() { print("...this is some clean exhaust...") } } public class Car<F: Fuel,E: Exhaust>: Automobile where F.ExhaustType == E { public func drive(fuel: F) -> E { return fuel.consume() } } public class Car1<F: Fuel>: Automobile { public func drive(fuel: F) -> F.ExhaustType { return fuel.consume() } }
具體的使用狀況以下:spa
var car = Car<UnleadedGasoline<CleanExhaust>, CleanExhaust>() car.drive(fuel: UnleadedGasoline<CleanExhaust>()).emit() var fusion = Car1<UnleadedGasoline<CleanExhaust>>() fusion.drive(fuel: UnleadedGasoline<CleanExhaust>()).emit()
轉換成C#代碼的話,有兩種思路,首先是把泛型參數放到Interface層面:code
public interface Automobile<FuelType, ExhaustType> { ExhaustType Drive(FuelType fuel); } public interface Fuel<ExhaustType> { ExhaustType consume(); } public interface Exhaust { void Emit(); } public class UnleadedGasoline<Exhaust> : Fuel<Exhaust> where Exhaust : new() { public Exhaust consume() { Console.WriteLine("...consuming unleaded gas..."); return new Exhaust(); } } public class CleanExhaust : Exhaust { public void Emit() { Console.WriteLine("...this is some clean exhaust..."); } } public class Car : Automobile<UnleadedGasoline<CleanExhaust>, CleanExhaust> { public CleanExhaust Drive(UnleadedGasoline<CleanExhaust> fuel) { return fuel.consume(); } }
還能夠模仿Swift對Automobile多作一層繼承進行包裝:blog
public interface Car1<T1> : Automobile<UnleadedGasoline<T1>, T1> where T1 : new() { } public class SimpleCar : Car1<CleanExhaust> { public CleanExhaust Drive(UnleadedGasoline<CleanExhaust> fuel) { return fuel.consume(); } }
調用的時候沒有什麼太大的差異:繼承
var gaso = new UnleadedGasoline<CleanExhaust>(); var car = new Car(); car.Drive(gaso).Emit(); var simpleCar = new SimpleCar(); simpleCar.Drive(gaso).Emit();
和Swift比較不一樣的是,咱們在Interface就代入了泛型參數。可是因爲咱們不能直接實例化Interface,因此並不能直接使用Automobile來減小一層繼承關係。接口
由於上述提到的使用associatedtype 的第一點理由見仁見智,這裏不分高下。
C#還有第二種思路,就是我也把泛型約束下放到Func層級:
public interface Automobile { ExhaustType Drive<FuelType,ExhaustType>(FuelType fuel) where ExhaustType : new(); } public interface Fuel { ExhaustType consume<ExhaustType>() where ExhaustType : new(); } public class UnleadedGasoline : Fuel { public Exhaust consume<Exhaust>() where Exhaust : new() { Console.WriteLine("...consuming unleaded gas..."); return new Exhaust(); } } public class Car2 : Automobile { public CleanExhaust Drive<UnleadedGasoline, CleanExhaust>(UnleadedGasoline fuel) where CleanExhaust : new() { return (fuel as Fuel).consume<CleanExhaust>(); } }
C#的接口並不能定義構造函數。強行模仿起來還真是有點累啊。最終的使用也很簡單:
var fuel = new UnleadedGasoline(); var car2 = new Car2(); car2.Drive<UnleadedGasoline,CleanExhaust>(fuel).Emit();
通篇比較下來,應該說Swift經過associatedtype 關鍵字和<Type>的混用,使得泛型的定義更爲複雜也更靈活了。
GitHub: