【问题标题】:Conforming to generic protocol method returning PATs符合返回 PAT 的通用协议方法
【发布时间】:2018-12-04 07:02:09
【问题描述】:

我想声明类似于以下的通用协议:

protocol Factory {
    func createWidget<T, TWidget>(_ t: T) -> TWidget 
    where TWidget: Widget, TWidget.T == T
}

protocol Widget {
    associatedtype T
    func get() -> T
}

我希望我可以实现 Factory 的具体变体,通过隐藏的实现返回它们自己的具体和不透明的 Widget

这是一个构建失败的示例实现:

struct ConcreteFactory: Factory {
    func createWidget<T, TWidget>(_ t: T) -> TWidget 
    where TWidget: Widget, TWidget.T == T {
        // This line has an error…
        return ConcreteWidget(widgetValue: t)
    }
}

struct ConcreteWidget<T>: Widget {
    let widgetValue: T

    init(widgetValue: T) {
        self.widgetValue = widgetValue
    }

    func get() -> T {
        return widgetValue
    }
}

但是,这不会编译。

在指示的行中,Swift 的编译器给出了错误“无法将 'ConcreteWidget' 类型的返回表达式转换为 'TWidget' 类型的返回表达式”。

我也尝试让ConcreteFactory 返回ConcreteWidget,但错误是ConcreteFactory 不符合Factory

【问题讨论】:

    标签: swift generics swift-protocols pats


    【解决方案1】:

    这行不通。当您调用您的createWidget 方法时,您指定两种类型TTWidget

    struct MyWidget: Widget { 
       func get() -> Int { ... }
    }
    
    let widget: MyWidget = factory.createWidget(12)
    

    在此示例中,TWidgetMyWidgetTInt。这很好地说明了为什么你的方法行不通。您不能将ConcreteWidget&lt;Int&gt; 分配给MyWidget 类型的变量。

    您需要一个 type-eraser 用于您的小部件。目前你必须自己编写,但将来编译器有望在需要时自动生成它们。

    struct AnyWidget<T>: Widget {
        private let _get: () -> T
    
        init<Other: Widget>(_ other: Other) where Other.T == T {
            _get = other.get
        }
    
        func get() -> T {
            return _get()
        }
    }
    

    这允许您编写工厂协议和实现:

    protocol Factory {
        func createWidget<T>(_ t: T) -> AnyWidget<T>
    }
    
    struct ConcreteFactory: Factory {
        func createWidget<T>(_ t: T) -> AnyWidget<T> {
                return AnyWidget(ConcreteWidget(widgetValue: t))
        }
    }
    

    【讨论】:

    • 谢谢斯文! ......我已经去实施AnyWidget 与:struct AnyWidget&lt;Underlying: Widget&gt;: Widget {… let underlying: Underlying …}。它将所有Widget 关注点转发给它。 ……哈哈哈哈……大约 2 分钟后,我开始看到这个问题有点尴尬,因为您需要提供底层的 Widget 才能使用它。有没有办法使这种方法起作用?我不确定它是否真的能删除任何类型!
    • :-) 我很确定我的方法无法奏效,所以我完全同意你的方法。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    相关资源
    最近更新 更多