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

时间:2015-08-06 14:15:19

标签: swift generics 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
    }
}

上述代码导致错误" 协议' 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)?

1 个答案:

答案 0 :(得分:7)

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

但是如果someValueInt,你无法在编译时知道。 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的协议含糊不清,因此无法用作类型,但仅作为类型约束。

相关问题