使用符合协议的类型变量调用泛型函数

时间:2017-07-08 07:03:08

标签: swift generics

在泛型函数中,我想测试符合某个协议的给定对象是否属于给定类型。如果将具体的类类型作为参数传递给检查函数,它的效果很好。但是,当我使用变量作为类型(使用三元运算符)时,我收到一个错误:

  

无法使用类型为“isObject”的参数列表调用“(AnyObject, of: P.Type)

另外,将类型变量强制转换为P.Protocol也无济于事,因为:

  

在参数类型“P.Protocol”中,“P”不符合预期类型“P

protocol P {
    static var descr: String {get}
}

class A: P {
    static let descr = "class A"
}

class B: P {
    static let descr = "class B"
}

class Test {
    func isObject<T:P>(_ object: AnyObject, of type: T.Type) -> Bool {
        print("descr: \(type.descr)")
        return object is T
    }
}

let a = A()
let type = (false ? A.self : B.self) as P.Type //as! P.Protocol
let test = Test()

test.isObject(a, of: type)

1 个答案:

答案 0 :(得分:0)

问题是,对于通用占位符T,当T是协议类型P时,T.TypeP.Protocol 不是 P.Type。换句话说,它采用元描述来描述协议本身而不是描述符合协议的类型的元类型。这种区别很重要,因为protocols don't conform to themselves

在您的情况下,一个解决方案是在P.Type元类型周围引入一个包装器,它使用初始化器中的通用占位符来存储闭包以执行is检查。

struct AnyPType {

    let base: P.Type
    private let _isInstance: (Any) -> Bool

    /// Creates a new AnyType wrapper from a given metatype.
    /// The passed metatype's value **must** match its static value, i.e `T.self == base`.
    init<T : P>(_ base: T.Type) {
        precondition(T.self == base, "The static value \(T.self) and dynamic value \(base) of the passed metatype do not match")
        self.base = T.self
        self._isInstance = { $0 is T }
    }

    func isInstance(_ instance: Any) -> Bool {
        return _isInstance(instance)
    }
}

这是我在答案to this Q&A中展示的包装器的专用版本,其中我还展示了如何在Apple平台上解除T.self == base的限制(但此限制不应该是在你的情况下的问题)。

您现在可以像这样使用包装器:

class Test {
    func isObject(_ object: Any, of type: AnyPType) -> Bool {
        print("descr: \(type.base.descr)")
        return type.isInstance(object)
    }
}

let type = AnyPType(A.self) // or AnyPType(B.self)

print(Test().isObject(A(), of: type))

// descr: class A
// true