【问题标题】:Swift - Specify conformity to a protocol of a generic type parameterSwift - 指定符合泛型类型参数的协议
【发布时间】:2020-04-22 00:02:14
【问题描述】:

我想要做的是有两个泛型类型参数,其中一个是特定类型,另一个是协议,如下所示:

@propertyWrapper
struct Implementation<T, P> where T : AnyObject, T : P { // Compiler error

    var wrappedValue: P { projectedValue }

    var projectedValue: T

    init(_ instance: T) {
        self.projectedValue = instance
    }

}

这样,可以隐藏实际类型,只暴露协议。

现在这行不通了,因为P 是一个非类、非协议类型,所以T 不能被限制在它上面。

有没有办法解决这个问题?

【问题讨论】:

    标签: swift generics swift-protocols property-wrapper


    【解决方案1】:

    我认为你可以为 T 创建一个协议来继承,那么你根本不需要 P:

    protocol ImplementationProtocol: AnyObject {}
    
    @propertyWrapper
    struct Implementation<T: ImplementationProtocol> { 
    
        var wrappedValue: ImplementationProtocol { projectedValue }
    
        var projectedValue: T
    
        init(_ instance: T) {
            self.projectedValue = instance
        }
    
    }
    

    现在,您的“T”必须符合“ImplementationProtocol”,“wrappedValue”也必须符合“ImplementationProtocol”,正如您在上面的代码中尝试完成的那样。

    希望对你有帮助

    【讨论】:

    • 这和我想做的不一样。您只能将此属性包装器用于ImplementationProtocol
    • 好的,我做了一些挖掘,这个方法怎么样,它并不完美,因为 WrappedValue 可以为空,但它应该可以工作
    【解决方案2】:

    你想要的不是语言的特性,所以你最接近的选择是一个运行时解决方案,它否定了一些属性包装糖。

    @propertyWrapper
    struct Implementation<Object: AnyObject, Protocol> {
      init(_ projectedValue: Object) throws {
        if let error = CastError.Desired(projectedValue, Protocol.self)
        { throw error }
    
        self.projectedValue = projectedValue
      }
    
      var projectedValue: Object
      var wrappedValue: Protocol { projectedValue as! Protocol }
    }
    
    protocol Protocol { }
    class Class: Protocol { init() { } }
    struct Struct {
      @Implementation<Class, Protocol> var implementation: Protocol
    
      init() throws {
        _implementation = try .init( .init() )
      }
    }
    
    public enum CastError {
        /// An error that represents that an desired cast is not possible.
      public struct Desired<Instance, DesiredCast>: Error {
        /// `nil` if `instance` is a `DesiredCast`.
        /// - Parameter instance: Anything. ?
        public init?(_ instance: Instance, _: DesiredCast.Type) {
          if instance is DesiredCast
          { return nil }
        }
      }
    
      /// An error that represents that an undesired cast is possible.
      public struct Undesired<Instance, UndesiredCast>: Error {
        /// `nil` if `instance` is not an `UndesiredCast`.
        /// - Parameter instance: Anything. ?
        /// - Note: Ineffective if `instance` is a protocol instance
        /// and `UndesiredCast` is `AnyObject`.
        public init?(_ instance: Instance, _: UndesiredCast.Type) {
          guard type(of: instance) is UndesiredCast.Type
          else { return nil }
        }
      }
    }
    

    【讨论】:

    • 是的,我想这是我们在当前语言限制下能得到的最接近的结果。太糟糕了:(
    【解决方案3】:
    @propertyWrapper
    struct Implementation<T, P> where T : AnyObject{ 
    var wrappedValue: P? = nil
    
    var projectedValue: T {
        didSet {
            if let value =  projectedValue as? P {
                    wrappedValue = value
                }
            }
        }
    
        init(_ instance: T) {
            self.projectedValue = instance
        }
    
    }
    

    【讨论】:

    • 这也是有限的,因为没有办法强制T 也是P 类型。这与删除T 和制作instance: AnyObject 基本相同。不过感谢您的尝试。
    猜你喜欢
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多