有什么方法可以确定子协议的一致性,例如符合 Swift 2 中的通用协议(在运行时或编译期间)?

Is there any way to determine sub-protocol conformance for instance conforming to generic protocol in Swift 2 (at runtime or during compilation)?

我正在将 Java 库移植到 Swift 2.0,但在使用泛型时遇到了一些问题。

我有以下协议层次结构:

public protocol Graph {
    typealias V: Hashable
    typealias E: Hashable

    func getAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>?
    func getEdge(sourceVertex: V, targetVertex: V) -> E?
    func getEdgeFactory() -> EdgeFactory<V, E>?
    func addEdge(sourceVertex: V, targetVertex: V) -> E?
    func addEdge(sourceVertex: V, targetVertex: V, e: E) -> Bool
    func addVertex(v: V) -> Bool
    func containsEdge(sourceVertex: V, targetVertex: V) -> Bool
    func containsEdge(e: E) -> Bool
    func containsVertex(v: V) -> Bool
    func edgeSet() -> Set<E>
    func edgesOf(v: V) -> Set<E>
    func removeAllEdges<T: CollectionType where T.Generator.Element == E>(edges: T) -> Bool
    func removeAllEdges(sourceVertex: V, targetVertex: V) -> Set<E>?
    func removeAllVertices<T: CollectionType where T.Generator.Element == V>(vertices: T) -> Bool
    func removeEdge(sourceVertex: V, targetVertex: V)
    func removeEdge(e: E) -> Bool
    func removeVertex(v: V) -> Bool
    func vertexSet() -> Set<V>
    func getEdgeSource(e: E) -> V
    func getEdgeTarget(e: E) -> V
    func getEdgeWeight(e: E) -> Double
}

public protocol DirectedGraph: Graph {
    func inDegreeOf(vertex: V) -> Int
    func incomingEdgesOf(vertex: V) -> Set<E>
    func outDegreeOf(vertex: V) -> Int
    func outgoingEdgesOf(vertex: V) -> Set<E>
}

public protocol UndirectedGraph: Graph {    
    func degreeOf(vertex: V) -> Int
}

下面是 class 的定义,这会引起麻烦:

public class CrossComponentIterator
    <V: Hashable, E: Hashable, D, G: Graph
        where G.V == V, G.E == E>
    : AbstractGraphIterator<V, E>

也就是说,它有一种方法可以根据传递的图形的实际类型(DirectedGraph 或 UndirectedGraph)初始化其变量之一。

我已经尝试通过声明多个版本的函数来解决这个问题:

func createGraphSpecifics<DG: Graph where DG: DirectedGraph, DG.V == V, DG.E == E>(graph: DG)
    -> CrossComponentIteratorSpecifics<V, E>
{
    return DirectedSpecifics<V, E, DG>(graph: graph)
}

func createGraphSpecifics<UG: Graph where UG: UndirectedGraph, UG.V == V, UG.E == E>(graph: UG)
    -> CrossComponentIteratorSpecifics<V, E>
{
    return UndirectedSpecifics<V, E, UG>(graph: graph)
}

func createGraphSpecifics<GG: Graph where GG.V == V, GG.E == E>(graph: GG)
    -> CrossComponentIteratorSpecifics<V, E>
{
    fatalError("Unknown graph type instance")
}

但不幸的是,任何图形实例都只调用最新版本的函数(即使它符合 "DirectedGraph" 或 "UndirectedGraph")

而且我知道,我可能可以通过将协议 DirectedGraph 和 UndirectedGraph 转换为抽象协议来解决这个问题 classes (我的意思是 classes 在每个声明的函数中都有 fatalError() 因为 Swift 不支持抽象 classes de jure。

但也许还有另一种更优雅且 Swifty 的解决方案?

在 Java 中这是微不足道的 - 在运行时检查接口的一致性:

if (g instanceof DirectedGraph<?, ?>) {
    return new DirectedSpecifics<V, E>((DirectedGraph<V, E>) g);
} else {
    return new UndirectedSpecifics<V, E>(g);
}

编辑 这是我想要实现的最少代码:

protocol P {
    // This typealias makes impossible to use 'P'
    // (or its descendants) as a type.
    // It can be used only as generic constraint.
    typealias A

    // myfunc is needed for compiler to infer 'A'
    func myfunc(a: A)
}
protocol P1:P {
    func p1specific(a: A)
}
protocol P2:P {
    func p2specific(a: A)
}

struct S<T:P> {
    init(t: T) {
        // TODO: check if 't' conforms to 'P1', 'P2', both or neither
    }
}

// Examples of concrete implementations of 'P1' and 'P2'
struct S1<X>:P1{
    func myfunc(a: X) {}
    func p1specific(a: X) {}
}
struct S2<X>:P2{
    func myfunc(a: X) {}
    func p2specific(a: X) {}
}
import XCPlayground
import Foundation

protocol P {}
protocol P1:P {}
protocol P2:P {}

struct S1:P1{}
struct S2:P2{}

struct S<T:P> {
    var p1: P1?
    var p2: P2?

    init(t: T) {
        p1 = t as? P1
        p2 = t as? P2
    }
}

let p1 = S1()
let p2 = S2()
let s1 = S(t: p1)
let s2 = S(t: p2)

dump(s1)
dump(s2)
/*
▿ S<S1>
  ▿ p1: S1
    - Some: S1
  - p2: nil
▿ S<S2>
  - p1: nil
  ▿ p2: S2
    - Some: S2
*/

使用

g is Type             // trur or false

let v2 = v1 as? Type   // v2 = v2 or nil

在swift 更新

protocol P {
    typealias A
}
protocol P1:P {}
protocol P2:P {}

struct S1:P1{
    typealias A = Int
}
struct S2:P2{
    typealias A = Double
}

struct S<T:P> {
    var p1: S1?
    var p2: S2?

    init(t: T) {
        p1 = t as? S1
        p2 = t as? S2
    }
}

let p1 = S1()
let p2 = S2()
let s1 = S(t: p1)
let s2 = S(t: p2)

.....

protocol P {
    // This typealias makes impossible to use 'P'
    // (or its descendants) as a type.
    // It can be used only as generic constraint.
    typealias A

    // myfunc is needed for compiler to infer 'A'
    func myfunc(a: A)
}
protocol P1:P {}
protocol P2:P {}

// aka 'abstract' conforming to P1
struct S1:P1{
    typealias A = AnyObject
    func myfunc(a: A) {}
}
// aka 'abstract' conforming to P2
struct S2:P2{
    typealias A = Int
    func myfunc(a: A) {}
}
// generic struct with type conforming to P
struct S<T:P> {
    init(t: T) {
        // TODO: check if 't' conforms to 'P1', 'P2', both or neither
        if t is S1 {
            print("t conforms to P1, because it is type S1")
        }
        if t is S2 {
            print("t conforms to P2, besause it is type S2")
        }
    }
}

let s1 = S(t: S1()) // t conforms to P1, because it is type S1
let s2 = S(t: S2()) // t conforms to P2, besause it is type S2

// WARNING !!!!!!
// var s = s1
// s = s2   // error: cannot assign value of type 'S<S2>' to type 'S<S1>'

Is there any way to determine sub-protocol conformance for instance conforming to generic protocol in Swift 2 (at runtime or during compilation)?

是。

这是我实现类型擦除的技巧,因此可以利用运行时。观察 _P:

protocol _P {
    static var _A: Any.Type { get }
    func _myfunc(_a: Any) -> Void?
}

extension _P where Self: P {

    static var _A: Any.Type {
        return A.self
    }

    func _myfunc(_a: Any) -> Void? {
        return (_a as? A).map(myfunc)
    }
}

protocol P {
    typealias A

    func myfunc(a: A)
}

protocol _P1:_P {
    func _p1specific(_a: Any) -> Void?
}

extension _P1 where Self: P1 {
    func _p1specific(_a: Any) -> Void? {
        return (_a as? A).map(p1specific)
    }
}

protocol P1:_P1, P {
    func p1specific(a: A)
}

protocol _P2:_P {
    func _p2specific(_a: Any) -> Void?
}

extension _P2 where Self: P2 {
    func _p2specific(_a: Any) -> Void? {
        return (_a as? A).map(p2specific)
    }
}

protocol P2:_P2, P {
    func p2specific(a: A)
}

您现在可以确定一个值是否符合 P1P2,并据此强制转换。此外,通用参数 A 现在可以通过不透明的 Any.Type 获得。

(x as? _P1) != nil ? true : false