• Swift泛型Protocol对比C#泛型Interface


      本篇纯属抬杠之作,之前我们提到了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();
      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

  • 相关阅读:
    Java 日志组件(二)
    Java 日志组件(一)
    spring基础——AOP(七)
    ionic cordova 友盟统计添加
    js 页面滑动时禁止触发touchend事件
    ios中iframe页面出现白屏问题
    小程序 onReachBottom 事件快速滑动时不触发的bug
    小程序 web-view 嵌套的网页跳转到小程序内部页面 实现无缝连接
    移动端click事件无反应或反应慢 touchend事件页面滑动时频繁触发
    ios ionic3 跳转第三方地图 xcode加入白名单
  • 原文地址:https://www.cnblogs.com/manupstairs/p/5980850.html
Copyright © 2020-2023  润新知