我试图做一些协议组合用于依赖注入,但是我遇到了一个我怀疑可能没有我想要的解决方案的问题,但是我无法看到它的逻辑原因:
protocol DM1 {
func sayHi() -> Void
}
protocol DM2 {
func sayHello() -> Void
}
protocol VM1 {
typealias T: DM1
var dm: T { get }
}
protocol VM2 {
typealias T: DM2
var dm: T { get }
}
protocol RT: VM1, VM2 {
}
class James {
let rt: RT
init(rt: RT) {
self.rt = rt
}
}
上述代码导致错误" 协议' RT'只能用作通用约束,因为它具有自我或相关的类型要求"在rt
实例变量和James
的实例化参数上。我真的不明白为什么我不能在James
课程中使用这个通用要求。
我最初做过类似的事情:
protocol DM1 {
func sayHi() -> Void
}
protocol DM2 {
func sayHello() -> Void
}
protocol VM1 {
var dm: DM1 { get }
}
protocol VM2 {
var dm: DM2 { get }
}
protocol RT: VM1, VM2 {
}
struct Fred: DM1, DM2 {
func sayHi() {
println("hi")
}
func sayHello() {
println("hello")
}
}
struct Bob: RT {
let dm: Fred
}
class James {
let rt: RT
init(rt: RT) {
self.rt = rt
}
}
但这失败了因为" 键入' Bob'不符合协议' VM1' " (和VM2
)我可以理解,因为我的协议要求变量属于特定的协议类型,而不是某个符合该协议的实例类型。因此,上述版本可以解决这个问题。
有没有人有我想做的解决方案(能够通过将RT
属性作为符合{{1}的具体结构来创建符合dm
的具体结构}和DM1
)?
答案 0 :(得分:7)
protocol RT
继承自protocol VM1
和protocol VM2
,两者都有typealias
个要求。
具有typealias
要求的协议只能用作类型约束,而不能用作类型。
即便是像这样的简单协议......
protocol MyProtocol {
typealias Empty
}
...(完全没用)只能用作类型约束。
this link处的某些信息可能会有所帮助。
编辑:
我会尽力解释为什么具有typealias
要求的协议只能用作类型限制。
将协议视为合同。该协议承诺提供名为promise
Int
的可变值:
protocol PromiseIntType {
var promise: Int { get set }
}
PromiseIntType
所做的合同是明确的,关于你所做的一切,你需要知道将它作为一个类型使用它。所以,如果你有一个这样的课......
class A {
var contract: PromiseIntType
}
......你知道如果你写这个......
let intValue = A().contract.promise
...... intValue
将是Int
。如果您想设置类型promise
属性的值,您知道需要为新值提供Int
:
let a = A()
a.contract.promise = 100
合同的所有条款都是事先知道的,您事先知道正在做出什么样的承诺以及您正在使用哪种类型。
PromiseIntType
可以完全像被定义为实际类型一样使用,如下所示:
struct PromiseInt {
var promise: Int
}
现在在混合中提出typealias
要求:
protocol PromiseSomeType {
typealias Surprise
var promise: Surprise { get set }
}
PromiseSomeType
有什么承诺?它说它将提供一个名为promise
的可变值,但它并没有告诉你该值的类型。所有它告诉你的是它提供的任何东西都是Surprise
。并非所有合同条款都是事先知道的。稍后将填写一些细节。
但这使得无法将PromiseSomeType
用作类型。看看这个课程,问问自己你可以用contract
属性做什么:
class B {
var contract: PromiseSomeType
}
例如,您将如何设置它?
let b = B()
b.contract.promise = <What type goes here?>
如果您尝试访问promise
属性,会获得什么类型的值?
let someValue = b.contract.promise // What type is someValue?
[补充编辑:
您将如何使用someValue
?如果它是Int
,那么你可以这样做:
let newValue = someValue + 12
但是如果someValue
是Int
,你无法在编译时知道。 Swift坚持在编译时知道每个常量,变量和对象的类型,以便它可以检查您对该类型执行的操作是否合法。如果将这些确定推迟到运行时,非法操作会使整个程序崩溃,我们将失去类型安全所带来的好处。
/附加编辑]
在创建履行合同的实际类型时,您需要填写PromiseSomeType
合约的详细信息:
struct PromiseInt: PromiseSomeType {
var promise: Int
}
PromiseInt
表示它将履行PromiseSomeType
合同,并填写promise
属性所属类型的缺失细节。
使用PromiseSomeType
作为类型限制似乎只是推动了模糊性:
class C<T: PromiseSomeType> {
var contract: T
}
在这种情况下,在创建泛型类型的实例并指定您正在使用的实际类型时,将填写合同的详细信息:
let c = C<PromiseInt>(contract: PromiseInt(promise: 0))
c.contract.promise = 100 // You know that promise is of type Int
无论哪种方式,在实际使用对象之前,必须知道其类型的所有细节。
我想重点是Swift是一种类型安全的语言。你不能创造模棱两可的类型。使用typealias
的协议含糊不清,因此无法用作类型,但仅作为类型约束。