为什么我的简单协议只能用作通用约束?

Why can my simple protocol only be used as a generic constraint?

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

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

以上代码导致错误“Protocol 'RT' can only be used as a generic constraint because it has Self or associated type requirements” on the rt James 的实例变量和实例化参数。我真的不明白为什么我不能在 James class.

中使用这个通用要求

我最初做了如下的事情:

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

但这失败了,因为“Type 'Bob' 不符合协议 'VM1'”(和 VM2)理解,因为我的协议要求变量是特定的协议类型,而不是符合该协议的某些实例类型。因此,上述版本旨在解决这个问题。

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

protocol RT 继承自 protocol VM1protocol VM2,它们都有 typealias 要求。

具有 typealias 要求的协议只能用作类型约束,不能用作类型。

即使是这样一个简单的协议...

protocol MyProtocol {
    typealias Empty
}

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

this link 上的一些信息可能会有帮助。

编辑:

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

将协议视为合同。该协议承诺提供一个名为 promise 且类型为 Int:

的可变值
protocol PromiseIntType {
    var promise: Int { get set }
}

PromiseIntType 制定的合同对于将其用作类型需要知道的所有内容都是明确的关于它做出的承诺。所以如果你有这样的 class...

class A {
    var contract: PromiseIntType
}

...你知道如果你写这个...

let intValue = A().contract.promise

...intValue 将是 Int。如果要设置类型的 promise 属性 的值,您知道需要为新值提供 Int

let a = A()
a.contract.promise = 100

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

A PromiseIntType 可以像被定义为实际类型一样使用,如下所示:

struct PromiseInt {
    var promise: Int
}

现在在组合中提出 typealias 要求:

protocol PromiseSomeType {
    typealias Surprise
    var promise: Surprise { get set }
}

PromiseSomeType 做了什么承诺?它说它将提供一个名为 promise 的可变值,但它没有告诉您该值是什么类型。它只告诉您,无论它提供什么,都将是 Surprise。并非合同的所有条款都是事先已知的。后面会补一些细节。

但这使得无法使用 PromiseSomeType 作为类型。看看这个 class,然后问问自己可以用 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 的协议是不明确的,因此不能用作类型,而只能用作类型约束。