简体   繁体   English

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

[英]Invoking a generic function with a type variable conforming to a protocol

In a generic function, I want to test if a given object conforming to a certain protocol is of a given type. 在一个泛型函数中,我想测试符合某个协议的给定对象是否为给定类型。 It works great if a concrete class type is passed as a parameter to the checking function. 如果将具体的类类型作为参数传递给检查功能,则效果很好。 However, when I use a variable for the type (using ternary operator), I get an error: 但是,当我为类型使用变量(使用三元运算符)时,出现错误:

Cannot invoke ' isObject ' with an argument list of type ' (AnyObject, of: P.Type) ' 无法使用类型为“ (AnyObject, of: P.Type) ”的参数列表调用“ isObject

Casting additionally the type variable to P.Protocol doesn't help either, since: 另外,将类型变量P.ProtocolP.Protocol也无济于事,因为:

In argument type ' P.Protocol ', ' P ' does not conform to expected type ' P ' 在参数类型“ 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)

The problem is that with a generic placeholder T , when T is a protocol type P , T.Type is P.Protocol not P.Type . 问题是,与通用占位符T ,当T是协议类型PT.TypeP.Protocol P.Type In other words, it takes a metatype that describes the protocol itself rather than a metatype that describes a type that conforms to the protocol. 换句话说,它采用描述协议本身的元类型,而不是描述符合协议类型的元类型。 This distinction is important because protocols don't conform to themselves . 这种区别很重要,因为协议不符合自己

One solution in your case is to introduce a wrapper around a P.Type metatype which uses a generic placeholder in the initialiser in order to store a closure to perform the is check for you. 您的情况下的一种解决方案是在P.Type元类型周围引入包装器,该包装器在P.Type程序中使用通用占位符,以便为您存储闭包以执行is check。

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)
    }
}

This is a specialised version of the wrapper that I show in my answer to this Q&A , in which I also show how to lift the limitation of T.self == base on Apple platforms (but this limitation shouldn't be a problem in your case). 这是我在对此问答的回答中展示的包装程序的专用版本,在该版本中,我还展示了如何在Apple平台上解除T.self == base的限制(但此限制不应成为您的问题)案件)。

You can now use the wrapper like so: 您现在可以像这样使用包装器:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM