【问题标题】:Make a protocol conform to another protocol使一个协议符合另一个协议
【发布时间】:2018-11-30 16:04:24
【问题描述】:

我有两个协议:PenInstrumentForProfessional。我想让任何 Pen 成为 InstrumentForProfessional

protocol Pen {
  var title: String {get}
  var color: UIColor {get}
}

protocol Watch {} // Also Instrument for professional
protocol Tiger {} // Not an instrument

protocol InstrumentForProfessional {
  var title: String {get}
}

class ApplePen: Pen {
  var title: String = "CodePen"
  var color: UIColor = .blue
}

extension Pen: InstrumentForProfessional {} // Unable to make ApplePen an Instument for Professional: Extension of protocol Pen cannot have an inheritance clause

let pen = ApplePen() as InstrumentForProfessional

【问题讨论】:

    标签: ios swift protocols


    【解决方案1】:

    这是您在扩展中要求遵守协议的方式。

    extension Pen where Self: InstrumentForProfessional {}
    

    您当前的做法使编译器认为您在进行继承,而不是协议一致性。

    还要注意let pen = ApplePen() as InstrumentForProfessional 没有意义,不会编译。

    【讨论】:

    • 感谢您的回复。 let pen = ApplePen() as InstrumentForProfessional 会在应用补丁后编译吗?
    • 我的意思是将所有Pens 设置为InstrumentForProfessional
    • @paper1111,它不是一致性,结构/类和协议之间存在一致性,协议之间存在继承。已经打开官方手册了,上面写着“Protocol Inheritance”。
    • @RichardTopchiy let pen = ApplePen() as InstrumentForProfessional 仍然无法编译。您不能将类型转换为协议。使用extension后,Pens InstrumentForProfessional
    • @paper1111,不,它没有。您的扩展只允许指定符合PenInstrumentForProfessional 的所有类型都将具有的一些代码,而不是所有符合Pen 的类型都符合InstrumentForProfessional。并且将类型的值转换为类型符合的协议是有效的构造(假设协议没有关联的类型)。
    【解决方案2】:

    Protocols can inherit each other:

    协议继承

    一个协议可以继承一个或多个其他协议,并且可以在它继承的要求之上添加更多要求。协议继承的语法类似于类继承的语法,但可以选择列出多个继承的协议,用逗号分隔:

    protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
        // protocol definition goes here
    }
    

    所以,你基本上需要这样做:

    protocol InstrumentForProfessional {
        var title: String {get}
    }
    
    protocol Pen: InstrumentForProfessional {
        var title: String {get} // You can even drop this requirement, because it's already required by `InstrumentForProfessional`
        var color: UIColor {get}
    }
    

    现在符合Pen 的所有内容也符合InstrumentForProfessional

    【讨论】:

    • 是的,但是 OP 正在使用扩展。
    • @paper1111,那又怎样他展示了他尝试过的方法,但没有奏效。他特别要求能够使所有Pens 也成为InstrumentForProfessional。这就是我的回答。
    • 我想,这就是要走的路。我更喜欢做一个扩展,所以这两个协议是完全独立的。
    • @RichardTopchiy,好吧,你不能同时拥有协议独立性并确保所有符合一个的类型都符合另一个。你不能一边吃蛋糕一边吃。
    • 感谢您的建议,但@user28434 您的居高临下的语气不受欢迎。让我们彼此友好
    【解决方案3】:

    已经提供了两个答案:@user28434 为您提供了一个解决方案,假设您可以在编写 Pen 协议时添加一致性,@paper1111 为您提供了添加的机会到 Pen 扩展,该类型也符合 InstrumentForProfessional。注意:要利用@paper1111 的答案,您还必须将协议添加到您的类型中,如下所示:

    class ApplePen: Pen, InstrumentForProfessional {
      var title: String = "CodePen"
      var color: UIColor = .blue
    }
    

    这似乎比@user28434 的答案更偏离您的要求,实际上是在回答一个不同的问题(即如何为采用两种不同协议的类型添加功能)。因此,我会问您是否真正要寻找的不是协议而是类继承:

    class InstrumentForProfessional {
        var title: String
        init(title:String) {
            self.title = title
        }
    }
    
    class Pen: InstrumentForProfessional {
        var color: UIColor
        init(title:String, color:UIColor) {
            self.color = color
            super.init(title: title)
        }
    }
    

    因为看起来你通过title 属性的存在得到的是类继承常见的覆盖行为。那么问题就变成了,当您使用class 而不是structenum 时,为什么还要努力将类继承压缩到协议中?

    如果您不想应用类继承并且不想在编写Pen 协议时添加继承,并且如果您也不想向您的类添加多个协议,那么您可以为整洁做的另一件事是使用类型别名:

    protocol InstrumentForProfessional {
        var title: String {get}
    }
    
    protocol PenExtra {
        var color: UIColor {get}
        var title: String {get}
    }
    
    typealias Pen = InstrumentForProfessional & PenExtra
    
    class ApplePen: Pen {
        var title = "CodePen"
        var color = UIColor.blue
    }
    

    但是写完所有这些,如果你能按照@user28434的方法去做,那就去做吧。

    【讨论】:

      【解决方案4】:

      我认为如果您查看这个 SO 答案,它可以解决同样的问题。

      https://stackoverflow.com/a/37353146/1070718

      @paper1111 与您正在寻找的内容很接近,但我认为您确实想要这样做:

      extension InstrumentForProfessional where Self: Pen {}
      

      既然 Pen 已经符合 InstrumentForProfessional,那么当它是 Pen 时,您只需扩展 InstrumentForProfessional。

      有时我忘记了协议继承在 Swift 中是如何工作的,但感谢 SO 刷新了我的记忆。

      【讨论】:

        【解决方案5】:

        以上所有答案都解释了如何这样做,但没有解释为什么

        在考虑协议时,您必须在心理上进行转换 - 协议不是结构。当您定义协议一致性时,您只需提供符合类型必须打包的一组必需的东西。给出或接受该类型将如何实现它们。

        protocol InstrumentForProfessional {
          var title: String {get}
        }
        
        protocol Pen: InstrumentForProfessional {
          var title: String {get}
          var color: UIColor {get}
        }
        
        protocol Watch: InstrumentForProffesional {}
        protocol Tiger {} // Not an instrument
        

        笔不符合 InstrumentForProfessional。它 InstrumentForProfessional。另一个例子是拥有 Instrument 协议和 StringInstrument。你知道,StringInstrument 不符合 Instrument;它一种乐器。

        我认为您正在寻找的是某些字段的默认实现。考虑这个例子;每个 MovingObject 都应该告诉它的最大速度。汽车是运动物体吗?是的。它应该符合 MovingObject 吗? 不!

        protocol MovingObject {
            /// Top speed a vehicle can reach in km/h.
            var topSpeedKMH: Double { get }
        }
        
        protocol Car: MovingObject {
            /// Horsepower of a car.
            var hp: Double { get }
            var weight: Double { get }
            
            // Note that topSpeed is also required,
            // but since we specified it in MovingObject we don't have
            // to rewrite it here.
            // var topSpeedKMH: Double { get }
        }
        

        但我们知道,我们可以根据马力和重量来计算最高速度。这就是我们创建默认实现的原因。

        extension Car {
            var topSpeedKMH: Double {
                hp * weight
            }
        }
        

        现在,每辆汽车都可以“开箱即用”地符合 MovingVehicle;然而,它仍然可以为每个给定字段提供自己的实现。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-21
          相关资源
          最近更新 更多