我试图找出如何定义一个具有以下功能的函数 两个参数:
例如,给定
protocol P { }
class C : P { } // Class, conforming to P
class D { } // Class, not conforming to P
struct E: P { } // Struct, conforming to P
这应该编译:
register(P.self, obj: C()) // (1)
但这些不应该编译:
register(P.self, obj: D()) // (2) D does not conform to P
register(P.self, obj: E()) // (3) E is not a class
如果我们放弃第二个参数是类实例的条件,那么很容易:
func register<T>(proto: T.Type, obj: T) {
// ...
}
但这也会接受(3)
中的struct(值类型)。
这看起来很有希望并且编译
func register<T: AnyObject>(proto: T.Type, obj: T) {
// ...
}
但是(1)
,(2)
,(3)
都不再编译,例如。
register(P.self, obj: C()) // (1)
// error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
我假设编译器错误的原因与中的相同 Protocol doesn't conform to itself?
另一次失败的尝试是
func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
// error: non-protocol type 'T' cannot be used within 'protocol<...>'
一个可行的替代方案是一个以参数
为参数的函数这里的问题是如何限制第一个参数 类协议被接受。
背景:我最近偶然发现了
SwiftNotificationCenter
实现面向协议的类型安全通知机制的项目。
它有一个
register
看起来像这样的方法:
public class NotificationCenter {
public static func register<T>(protocolType: T.Type, observer: T) {
guard let object = observer as? AnyObject else {
fatalError("expecting reference type but found value type: \(observer)")
}
// ...
}
// ...
}
然后将观察者存储为弱引用,这就是他们的原因 必须是引用类型,即类的实例。 但是,仅在运行时检查,我想知道如何使其成为编译时检查。
我错过了一些简单/明显的东西吗?
答案 0 :(得分:0)
你无法直接做你想做的事。它与引用类型无关,因为任何约束都会使P.self: P.Protocol
存在,因此当您引用协议的元类型时,无法在呼叫站点满足它们。 {1}}和采用者C
。有一种特殊情况,T
不受限制,允许它首先工作。
到目前为止,更常见的情况是约束T: P
并要求P: class
,因为您可以使用任意协议的元类型执行仅事项将名称转换为字符串。它恰好在这种狭隘的情况下很有用,但就是这样;签名也可能是register<T>(proto: Any.Type, obj: T)
所有的好处。
理论上,Swift可以支持对元类型的约束,register<T: AnyObject, U: AnyProtocol where T.Type: U>(proto: U, obj: T)
但是我怀疑它在许多情况下会有用。