【问题标题】:Constraint generic function by comparing instance property type with generic parameter通过比较实例属性类型和泛型参数来约束泛型函数
【发布时间】:2019-07-27 03:59:05
【问题描述】:

我想继承 ViewControllers 来创建一个通用的 Coordinator 类。这个协调器类应该能够安全地将依赖注入到子协调器类。子协调员应该只能访问明确定义的依赖项。我用抽象类制作了以下工作游乐场来布局问题。我对如何解决所描述问题的其他想法持开放态度。

先决条件

import Foundation

protocol HasFirstDependency {
    var first: Any? { get }
}

protocol HasSecondDependency {
    var second: Any? { get }
}

typealias AllDependencies = HasFirstDependency & HasSecondDependency

struct AppDependencies: AllDependencies {
    var first: Any?
    var second: Any?
}



class Coordinator<D> {
    var dependencies: D?
}

extension Coordinator {

    static func initialize() -> Coordinator<D> {
        return Coordinator<D>()
    }

}

class ParentCoordinator: Coordinator<AllDependencies> {
    var children: [Any] = []
}

class FirstChildCoordinator: Coordinator<HasFirstDependency> {}

class SecondChildCoordinator: Coordinator<HasSecondDependency> {}

问题

以下代码概述了该问题(参见 cmets 1. 和 2.)。有没有办法在编译时以上述方式避免强制转换和限制 childType?

extension ParentCoordinator {

    func add<C: Coordinator<D>, D>(childType: C.Type) { // 2. ... by setting a constraint like this: "where self.dependecies is D?"
        let child = C.initialize()
        children.append(child)
        if let dependencies: D? = self.dependencies as? D? { // 1. is there any way to avoid this casting ...
            child.dependencies = dependencies
        } else {
            assertionFailure("parentCoordinator does not hold child dependencies")
        }
    }

}

let parent = ParentCoordinator()
parent.dependencies = AppDependencies(first: "bla", second: "blup")
parent.add(childType: FirstChildCoordinator.self)
let child = parent.children.first as? Coordinator<HasFirstDependency>
print(type(of: parent.dependencies)) // Optional<HasFirstDependency & HasSecondDependency>
print(parent.dependencies?.first) // Optional("bla")
print(parent.dependencies?.second) // Optional("blup")
print(type(of: child?.dependencies)) // Optional<HasFirstDependency>
print(child?.dependencies?.first) // Optional("bla")
//print(child?.dependencies?.second) // does not compile, what I wanted to achieve

我想要什么

更具体地说:以下致命错误是,我想在编译时捕获。

protocol ForgottenDependency {
    var forgotten: Any? { get }
}

class ThirdChildCoordinator: Coordinator<ForgottenDependency> {}

parent.add(childType: ThirdChildCoordinator.self) // Fatal error: parentCoordinator does not hold child dependencies: file MyPlayground.playground, line 49

【问题讨论】:

  • 我是否理解正确,您真正想要的是将ParentCoordinatorchildren 属性定义为[Coordinator&lt;AnyDependency&gt;],其中AnyDependency 是某种D1 || D2 || D3 类型?例如,如果您引入了一个新的依赖项 D4,那么编译器会阻止您添加 Coordinator&lt;D4&gt; 子项?
  • 没错! :) 感谢您的准确澄清。

标签: swift generics generic-programming


【解决方案1】:

您要求的是某种逻辑上的 OR 类型约束:

... where D == HasFirstDependency || HasSecondDependency

类型约束的析取在 swift 中是不可能的。引用commonly rejected changes

类型约束中的析取(逻辑 OR):这些包括匿名的类联合类型(例如 (Int | String) 表示可以由整数或字符串占据的类型)。 “[这种类型的约束] 类型系统不能也不应该支持。”

一种可能的解决方案是使依赖项符合通用协议。老实说,这并不能完全解决检测“被遗忘”依赖项的问题,因为所有依赖项都必须实现这个通用协议。这种方法在here进行了讨论。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多