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

Rup*_*ert 3 generics swift swift-protocols

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码导致错误" 协议'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
    }
}
Run Code Online (Sandbox Code Playgroud)

但这失败是因为" 类型'Bob'不符合协议'VM1' "(和VM2)我可以理解,因为我的协议要求变量属于特定协议类型而不是某些符合该类型的实例类型协议.因此,上述版本意味着解决这个问题.

有没有人有什么我想做一个解决方案(能够使这符合混凝土结构RT具有一个dm符合双方的具体结构特性DM1DM2)?

Aar*_*sen 7

protocol RT继承自protocol VM1protocol VM2,都有typealias要求.

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

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

protocol MyProtocol {
    typealias Empty
}
Run Code Online (Sandbox Code Playgroud)

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

此链接中的某些信息可能会有所帮助.

编辑:

我会尽力解释为什么带有typealias要求的协议只能用作类型约束.

将协议视为合同.该协议承诺提供一个名为promisetype 的可变值Int:

protocol PromiseIntType {
    var promise: Int { get set }
}
Run Code Online (Sandbox Code Playgroud)

对于PromiseIntType您需要知道的所有内容,明确表示将其用作与其承诺相关的类型. 所以如果你有这样的课......

class A {
    var contract: PromiseIntType
}
Run Code Online (Sandbox Code Playgroud)

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

let intValue = A().contract.promise
Run Code Online (Sandbox Code Playgroud)

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

let a = A()
a.contract.promise = 100
Run Code Online (Sandbox Code Playgroud)

合同的所有条款都是事先知道的,您事先知道正在做出哪些承诺以及您正在使用哪种类型的承诺.

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

struct PromiseInt {
    var promise: Int
}
Run Code Online (Sandbox Code Playgroud)

现在提出typealias要求:

protocol PromiseSomeType {
    typealias Surprise
    var promise: Surprise { get set }
}
Run Code Online (Sandbox Code Playgroud)

有什么承诺PromiseSomeType呢?它表示它将提供一个名为的可变值promise,但它不会告诉您该值的类型.所有它告诉你的是它提供的任何东西都是Surprise.并非所有合同条款都是事先知道的.稍后将填写一些细节.

但这使得它无法PromiseSomeType用作一种类型.看看这个课程,问问自己你可以用这个contract属性做什么:

class B {
    var contract: PromiseSomeType
}
Run Code Online (Sandbox Code Playgroud)

例如,您将如何设置它?

let b = B()
b.contract.promise = <What type goes here?>
Run Code Online (Sandbox Code Playgroud)

如果您尝试访问该promise属性,您会获得什么类型的值?

let someValue = b.contract.promise // What type is someValue?
Run Code Online (Sandbox Code Playgroud)

[附加编辑:

你会怎么用someValue?如果它是Int,那么你可以这样做:

let newValue = someValue + 12
Run Code Online (Sandbox Code Playgroud)

但是你无法在编译时知道是否someValueInt.Swift坚持在编译时知道每个常量,变量和对象的类型,以便它可以检查您对该类型执行的操作是否合法.如果它将这些确定推迟到运行时,非法操作会使整个程序崩溃,我们将失去类型安全提供的好处.

/附加编辑]

您在PromiseSomeType创建履行合同的实际类型时填写合同的详细信息:

struct PromiseInt: PromiseSomeType {
    var promise: Int
}
Run Code Online (Sandbox Code Playgroud)

PromiseInt说它将履行PromiseSomeType合同,并填写promise遗产的类型的遗漏细节.

使用PromiseSomeType类型约束似乎只是推动了模糊性:

class C<T: PromiseSomeType> {
    var contract: T
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,在创建泛型类型的实例并指定您正在使用的实际类型时,将填写合同的详细信息:

let c = C<PromiseInt>(contract: PromiseInt(promise: 0))
c.contract.promise = 100   // You know that promise is of type Int
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,在您实际使用对象之前,必须知道其类型的所有细节.

我想重点是Swift是一种类型安全的语言.您无法创建不明确的类型.使用a的协议typealias是不明确的,因此不能用作类型,而只能用作类型约束.