【问题标题】:Heterogeneous mixture of protocol types, including a generic protocol协议类型的异构混合,包括通用协议
【发布时间】:2016-07-03 01:15:07
【问题描述】:
 protocol ParentProtocol { }

 protocol ChildProtocol: ParentProtocol { }

 protocol Child_With_Value_Protocol: ParentProtocol {
     associatedType Value

     func retrieveValue() -> Value
 }

试图创建一个包含ChildProtocolChild_With_Value_Protocol bothParentProtocol 类型的单个数组。是否有任何可能的方法来创建一个循环遍历异构数组并返回仅类型为Child_With_Value_Protocol 的值的函数?

这可能需要架构更改。对所有解决方案开放。

尝试失败的解决方案 #1

var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]

func retrieveValues() -> [Any] {
    var values = [Any]()

    for parent in parents {
        if let childWithValue = parent as? Child_With_Value_Protocol { // Fails to compile
            values.append(childWithValue.retrieveValue())
        }
    }

    return values
}

这会失败并出现 protocol 'Child_With_Value_Protocol' can only be used as a generic constraint because it has Self or associated type requirements 错误,这是有道理的,因为编译器在转换为 Child_With_Value_Protocol 时不知道类型,这会导致下一个失败的解决方案。

尝试失败的解决方案 #2

如果数组是一个只有Child_With_Value_Protocol 的同构数组,则可以使用类型擦除来检索值。

var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]

struct AnyValue {
    init<T: Child_With_Value_Protocol>(_ protocol: T) {
        _retrieveValue  = protocol.retrieveValue as () -> Any
    }

    func retrieveValue() -> Any { return _retrieveValue() }

    let _retrieveValue: () -> Any
}

func retrieveValues() -> [Any] {
    var values = [Any]()

    for parent in parents {
        values.append(AnyValue(parent).retrieveValue()) // Fails to compile
    }

    return values
}

由于结构AnyValue 没有ParentProtocol 的初始化程序,因此编译失败。

尝试失败的解决方案 #3

struct AnyValue {
    init<T: Child_With_Value_Protocol>(_ protocol: T) {
        _retrieveValue  = protocol.retrieveValue as () -> Any
    }

    func retrieveValue() -> Any { return _retrieveValue() }

    let _retrieveValue: () -> Any
}

var erased: [AnyValue] = [AnyValue(...), AnyValue(...), AnyValue(...)]

func retrieveValues() -> [Any] {
    var values = [Any]()

    for value in erased {
        values.append(value.retrieveValue())
    }

    return values
}

与其他解决方案不同,此解决方案实际上可以编译。这个解决方案的问题在于数组erased 只能保存Child_With_Value_Protocol 的类型擦除版本的值。目标是让数组同时保存 Child_With_Value_ProtocolChildProtocol 的类型。

尝试失败的解决方案 #4

修改类型擦除结构以包含 ParentProtocol 的初始化程序仍会创建一个可编译的解决方案,但该结构将仅使用不太具体的 init,而不是更具体的 init。

struct AnyValue {
    init?<T: ParentProtocol>(_ protocol: T) {
        return nil
    }

    init?<T: Child_With_Value_Protocol>(_ protocol: T) {
        _retrieveValue  = protocol.retrieveValue as () -> Any
    }

    func retrieveValue() -> Any { return _retrieveValue() }

    let _retrieveValue: (() -> Any)?
}

【问题讨论】:

  • 这从一开始就注定要失败,这是不可能的,也没有任何意义。我相信您实际上甚至都不想要那个,也许您可​​以展示您的用例?
  • 您甚至尝试这样做的事实意味着您不应该将所有这些元素放在一个数组中。

标签: ios swift generics swift-protocols protocol-oriented


【解决方案1】:

前面的 cmets 可能是正确的。不过,您可以将变量装箱在一个枚举中并创建一个数组。然后引用将打开枚举值,每个都有正确类型的关联数据

编辑:我没有理会相关值,因为它似乎与所问的问题无关。以下在操场上工作:

protocol ParentProtocol: CustomStringConvertible {
    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol]
}

protocol ChildProtocol: ParentProtocol { }

protocol Other_Child_Protocol: ParentProtocol { }

enum FamilyBox {
    case Parent(parent: ParentProtocol)
    case Child(child: ChildProtocol)
    case OtherChildProtocol(withValue: Other_Child_Protocol)

}


var parents: [FamilyBox] = []

struct P: ParentProtocol {
    var description: String { return "Parent" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .Parent(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

struct C: ChildProtocol {
    var description: String { return "Child" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .Child(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

struct CV: Other_Child_Protocol {
    var description: String { return "Other Child" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .OtherChildProtocol(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

let p = FamilyBox.Parent(parent: P())
let c = FamilyBox.Child(child: C())
let cv = FamilyBox.OtherChildProtocol(withValue: CV())
let array:[FamilyBox] = [p, c, cv]

print(P.retrieveValues(array))
print(C.retrieveValues(array))
print(CV.retrieveValues(array))

最后三行的打印是:

[Parent]
[Child]
[Other Child]

虽然我确信它可以改进,但我认为符合最初的意图。没有?

【讨论】:

  • 如果您认为可以显示工作代码,但实际上并非如此,如果您能做到,我会感到惊讶
  • 无法通过此手机;我早上起床后会继续努力
  • @Kametrixom - 想法?
  • 是的,你是从“我没有为 associatedValue 烦恼”开始的,但这就是让它实际上不可能实现的部分
  • associatedType Value添加到协议中,可以看到无法编译了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多