Swift泛型Protocol對比C#泛型Interface

  本篇純屬擡槓之做,以前咱們提到了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

https://github.com/manupstairs/LearnSwift

https://github.com/manupstairs/LearnDotNetCore

相關文章
相關標籤/搜索