3

我正在尝试做一些用于依赖注入的协议组合,但我遇到了一个问题,我怀疑可能没有我想要的解决方案,但我看不到逻辑原因:

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'只能用作通用约束,因为它具有自身或关联的类型要求” 。我真的不明白为什么我不能在课堂上使用这个通用要求。rtJamesJames

我最初做了如下的事情:

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)我可以理解,因为我的协议要求变量是特定协议类型而不是某些符合该协议的实例类型协议。因此,上述版本旨在解决此问题。

有没有人对我想做的事情有解决方案(能够通过将一个同时符合and的具体结构RT作为dm属性来制作符合的具体结构)?DM1DM2

4

1 回答 1

7

protocol RT继承自protocol VM1protocol VM2,两者都有typealias要求。

typealias需求的协议只能作为类型约束,不能作为类型。

即使是像这样的简单协议......

protocol MyProtocol {
    typealias Empty
}

...(完全没用)只能用作类型约束。

此链接中有一些信息可能会有所帮助。

编辑:

我会尽力解释为什么带有typealias要求的协议只能用作类型限制。

将协议视为合同。该协议承诺提供一个名为promisetype的可变值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

合同的所有条款都是预先知道的,并且您预先知道正在做出什么样的承诺以及您正在使用什么类型。

APromiseIntType可以完全像定义为实际类型一样使用,如下所示:

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为 an 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 是一种类型安全的语言。您不能创建模棱两可的类型。使用 a 的协议typealias是不明确的,因此不能用作类型,而只能用作类型约束。

于 2015-08-06T21:28:42.650 回答