简体   繁体   English

Swift - 指定符合泛型类型参数的协议

[英]Swift - Specify conformity to a protocol of a generic type parameter

What I'm trying to do is to have two generic type parameters where one is a specific type and the other a protocol like so:我想要做的是有两个泛型类型参数,其中一个是特定类型,另一个是协议,如下所示:

@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
    }

}

This way, the actual type can be hidden and have only the protocol exposed.这样,可以隐藏实际类型,只暴露协议。

Now this doesn't work because P is a non-class, non-protocol type, so T cannot be constrained to it.现在这不起作用,因为P是非类、非协议类型,所以T不能被限制在它上面。

Is there a way around this?有没有解决的办法?

I think you can create a protocol for T to inherit from, then you wouldn't need the P at all:我认为你可以为 T 创建一个协议来继承,那么你根本不需要 P :

protocol ImplementationProtocol: AnyObject {}

@propertyWrapper
struct Implementation<T: ImplementationProtocol> { 

    var wrappedValue: ImplementationProtocol { projectedValue }

    var projectedValue: T

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

}

now your "T" will have to conform to "ImplementationProtocol" and "wrappedValue" will also have to conform to "ImplementationProtocol" as you tried to accomplish in your code above.现在您的“T”必须符合“ImplementationProtocol”,“wrappedValue”也必须符合“ImplementationProtocol”,正如您在上面的代码中试图完成的那样。

hope it helps希望能帮助到你

@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
    }

}

What you want is not a feature of the language, so your closest option is a runtime solution that negates some property wrapper sugar.你想要的不是语言的特性,所以你最接近的选择是一个运行时解决方案,它否定了一些属性包装糖。

@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 }
    }
  }
}

I believe you are approaching the entire problem in a way that is causing far too much abstraction, making it difficult to find a solution because the definition of the problem is not based in a real world use case.我相信您正在以一种导致太多抽象的方式处理整个问题,从而难以找到解决方案,因为问题的定义不是基于现实世界的用例。

Going back to basic principles.回到基本原则。 A protocol is defined as "a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality."协议被定义为“适合特定任务或功能的方法、属性和其他要求的蓝图”。

You want to define P as a protocol with some kind of function.您想将 P 定义为具有某种 function 的协议。

It would be good to take a step back and understand what it is you a trying to achieve with your property wrapper.退后一步并了解您尝试使用属性包装器实现的目标是一件好事。

Comment: Property wrappers are rarely effective and I have yet to see a good use of them outside very concrete cases, like SwiftUI.评论:属性包装器很少有效,我还没有看到在非常具体的情况下很好地使用它们,比如 SwiftUI。 They are a language extension feature that dramatically limits what you can do with a particular variable.它们是一种语言扩展功能,极大地限制了您可以对特定变量执行的操作。 Unless you have a really good architecture around it, property wrappers are rarely an effective choice in solving a problem.除非你有一个非常好的架构,否则属性包装器很少是解决问题的有效选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM