本篇纯属抬杠之作,之前我们提到了Swift的泛型Protocol使用associatedtype关键字,而不是使用<Type>语法的泛型参数。这其中有什么好处呢?
我就这个问题搜索了一些回答,大体上提到两点:
<Type>语法对Protocol没有意义,Protocol仅需要定义一个抽象的概念,具体的类型应该由实现的Class来明确,比如:
ClassWithInt<Int>: NumberProtocol
ClassWithDouble<Double>: NumberProtocol
associatedtype可以用来给Protocol中特定Func添加泛型约束,而不是限定整个Protocol
protocol GeneratorType {
associatedtype Element
public mutating func next() -> Self.Element?
}
听上去还是有一定道理的,然后实践是检验事实的唯一标准。下面我们通过代码实例来和C#进行对比。首先拿出网上多被引用解释上述两个观点的Swift代码:
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()
}
}
具体的使用情况如下:
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层面:
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多做一层继承进行包装:
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();