【问题标题】:Test if instance is a protocol with typealias in Swift?在 Swift 中测试实例是否是具有 typealias 的协议?
【发布时间】:2015-09-09 16:00:34
【问题描述】:

这与this question 有关。使用 typealias 我可以创建一个具有“子”变量但具有特定类型的类,这正是我正在寻找的。但是当我这样做时,我无法再测试一个实例是否遵循该协议。我也尝试过“如果让”,但不起作用。有没有办法做到这一点?

protocol Parent {
    typealias Child
    var children: [Child] { get set }
}

protocol Child {
    typealias Parent
    var parent: Parent { get set }

}

class Foo: Parent {
    var children = [Bar]()

    init(){}
}

class Bar: Child {
    var parent = Foo()
}

let testVar = Foo()
let cVar = Bar()

testVar.children.append(cVar)
cVar.parent = testVar

//I get the error here saying protocol is limited to generic constraints
if testVar is Parent { 

}

【问题讨论】:

  • 我实际上认为这没有多大意义(编译器有其原因)或用于某些事情。您可以根据您的意图扩展您的问题吗?
  • @Kametrixom 我有大约 5 个不同的类,它们具有相同的变量“children”,有些具有相同的变量“parent”。这些都有一些共同点,但有时我更喜欢检查它是否是父类而不是类型检查,这意味着它保证有一个我想要访问的“子”变量。

标签: swift generics swift2


【解决方案1】:

不幸的是,没有办法静态地解决这个问题(至少现在是这样)。因此,更动态的方法是拥有一个没有自关联类型要求的“超级”协议。这允许您在这些“超级”协议之间进行转换:

protocol _Parent {
    var anyChildren: [Any] { get set }
}
protocol Parent: _Parent {
    typealias Child
    var children: [Child] { get set }
}

extension Parent {
    // requirement of _Parent
    var anyChildren: [Any] {
        get {
            return children.map{ $0 as Any }
        }
        set {
            // use `as!` and `map` if you are sure that all elements are of type Child
            // or if you want a crash if not
            children = newValue.flatMap{ $0 as? Child }
        }
    }
}

protocol _Child {
    var anyParent: Any { get set }
}

protocol Child: _Child {
    typealias Parent
    var parent: Parent { get set }
}

extension Child {
    // requirement of _Child
    var anyParent: Any {
        get {
            return parent
        }
        set {
            parent = newValue as! Parent
        }
    }
}

现在您可以使用它们的属性来操作数据:

class Foo: Parent {
    var children = [Foo]()
}

let anything: Any = Foo()

// use `if let` and `as?` instead of `is` in order to have a more static type
if let aDynamicParent = anything as? _Parent {
    aDynamicParent.anyChildren.map{ $0 as! Foo } // cast the array back to its original type

    aDynamicParent.children = [Foo(), Foo(), Foo()]
}

【讨论】:

  • 从来没有想过这样做,但那会奏效。我想这是目前的一种解决方法,但仍然希望 Swift 的下一个版本能够实现这样的功能。
【解决方案2】:

如果Parent 包含符合Child 协议的对象数组并且Child 包含Parent 协议属性,则不需要ChildParent 类型别名。

protocol Parent {
    var children: [Child] { get set }
}

protocol Child {
    var parent: Parent { get nonmutating set }
}

class Foo: Parent {
    var children: [Child]
    init(children: [Child]) {
        self.children = children
        self.children.forEach { $0.parent = self }
    }
}

class Bar: Child {
    var parent: Parent
    init(parent: Parent) {
        self.parent = parent
        self.parent.children.append(self)
    }
}

let testVar = Foo(children: [Bar]())
let cVar = Bar(parent: testVar)

let parent = testVar as Parent 
let children = parent.children

【讨论】:

  • 但是如果我希望 Foo 的孩子只有 Bar 类型呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多