将支持的方法限制为 Swift 中的泛型参数类型 4

Limit supported methods to type of generic parameter in Swift 4

给定某个泛型类型的结构,我想将可以调用的方法限制为仅那些属于泛型类型的子类型的方法。

struct ViewBuilder<T:UIView> {

    let parent:UIView

    func createView() -> UIView {
        let view = UIView()
        parent.addSubview(view)
        return view
    }
}

extension ViewBuilder where T:UIButton {

    func createButton() -> UIButton {
        let button = UIButton()
        parent.addSubview(button)
        return button
    }
}

在这段代码中,我指定 builder 应该只创建 UIControls:

let builder = ViewBuilder<UIControl>(parent:UIView())
let view = builder.createView() // ok
let button = builder.createButton() //  'UIControl' is not a subtype of 'UIButton'

但是,如您所见,我得到了相反的行为; a UIView 而不是 a UIControl;一个UIButton一个UIControl。在 Swift 中有什么方法可以做到这一点? (我使用的是 Swift 4。)我已经尝试了一些使用泛型和协议的方法,但没有成功。

(我理解 为什么 上面的代码按它的方式工作,但我正在寻找替代方案来解决整个问题(如果存在的话)。如果有人可以提出一个好的解决方案我可以改进问题以适应。)

无法从子类型中删除方法(T where T:...T 的子类型)。这是类型的一个基本特征。子类型必须能够做类型能做的一切。否则,如果一个函数采用泛型 ViewBuilder<T>,它怎么知道是否允许 createView 呢?扩展是 扩展 。此类消费者甚至可能看不到它们。

原则上,这就是您真正要求的:

struct ViewBuilder<T: UIView> {

    let parent: UIView

    func create<U:T>() -> U {
        let view = U()
        parent.addSubview(view)
        return view
    }
}

这为 T 的子类型的任何类型提供了 create 方法,它本身必须是 UIView 的子类型。不幸的是,这目前是不合法的 Swift (SR-5213)。问题是受 class 约束的泛型类型参数本身不被视为 class 以约束其他类型参数。

考虑到这个限制,在大多数情况下我可能会使用这样的组合:

struct ViewBuilder {
    let parent: UIView

    func create<View: UIView>(_ type: View.Type) -> View {
        let view = View()
        parent.addSubview(view)
        return view
    }
}

struct ControlBuilder {
    private let builder: ViewBuilder
    var parent: UIView { return builder.parent }

    init(parent: UIView) {
        builder = BaseViewBuilder(parent: parent)
    }

    func create<Control: UIControl>(_ type: Control.Type) -> Control {
        return builder.create(type)
    }
}

let builder = ControlBuilder(parent:UIView())
let button = builder.create(UIButton.self)

这里是 ControlBuilder HASA ViewBuilder 而不是 ISA ViewBuilder。如果您想接受 "something that can create a kind of view",这有一些限制,因为由于 SR-5213,几乎不可能(据我所知)创建一个涵盖这两者的协议。但是根据您的示例,这看起来符合您的用例。

(不过,我对整个用例有点怀疑。我不清楚 "a view that can only contain controls" 有何用处。感觉你真正想要的是 UIView 的扩展。"Builder" 感觉像是在尝试将 Java 模式导入 Swift 中,但可能不合适。)