Swift中的多个类型约束


133

假设我有以下协议:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

现在,如果我想要一个采用通用类型的函数,但是该类型必须符合SomeProtocol我的要求,则可以执行以下操作:

func someFunc<T: SomeProtocol>(arg: T) {
    // do stuff
}

但是,有没有一种方法可以为多个协议添加类型约束?

func bothFunc<T: SomeProtocol | SomeOtherProtocol>(arg: T) {

}

类似的事情使用逗号,但是在这种情况下,它将开始不同类型的声明。这是我尝试过的。

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>

这是一个特别相关的问题,因为Swift文档没有在泛型章节中提及……
Bruno Philipe,2015年

Answers:


241

您可以使用where子句,该子句允许您指定所需的任意数量的要求(必须满足所有要求),并用逗号分隔

斯威夫特2:

func someFunc<T where T:SomeProtocol, T:SomeOtherProtocol>(arg: T) {
    // stuff
}

Swift 3和4:

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    // stuff
}

或更强大的where子句:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol{
    // stuff
}

您当然可以使用协议组合(例如protocol<SomeProtocol, SomeOtherProtocol>),但是灵活性稍差一些。

使用where可以处理涉及多种类型的情况。

您可能仍然想编写协议以便在多个地方重用,或者只是给组成的协议起一个有意义的名字。

斯威夫特5:

func someFunc(arg: SomeProtocol & SomeOtherProtocol) { 
    // stuff
}

由于协议离论点很近,因此感觉更自然。


真是不合逻辑,但是很高兴知道我只是想成为对此表示感谢的垃圾邮件发送者之一,自从我需要它后一个月就没有意识到这一点。
Mathijs Segers 2015年

3
有什么办法在类型约束表达式中对类和结构做同样的事情?例如<T where T:SomeStruct, T:AnotherStruct>?对于类,编译器似乎将其解释为“ T是两者的子类”,而对于结构,它只是抱怨"Type 'T' constrained to non-protocol type"
Jarrod Smith

对于OP中的特定示例,问题协议的组成应该是更可取的方法:上述解决方案是有效的,但是恕我直言,不必要地使功能签名混乱。此外,使用协议组合物,例如,一种类型的约束,仍然让你使用where子句附加类型/其它使用,例如func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }用于typealias SubType在例如SomeProtocol
dfri

1
似乎在swift3中已弃用,建议使用:func someFunc <T>(arg:T),其中T:SomeProtocol,T:SomeOtherProtocol {
Cristi Băluță

2
有没有办法告诉T必须是某种对象类型并实现某种协议?
Georg

73

您有两种可能性:

  1. 如Jiaaro的答案所示,您使用where子句

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
  2. 您使用协议组成类型

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }

2
imo第二个解决方案更漂亮,我想回答这个问题,它也更完整地提出了两种选择
Mathijs Segers 2015年

2
2号是在Swift 2下声明一个对我有效的唯一一个typealias。谢谢!
布鲁诺·菲利普

19

Swift 3.0的演进带来了一些变化。现在,我们的两个选择看起来有些不同。

where在Swift 3.0中使用子句:

where子句现已移至函数签名的末尾,以提高可读性。因此,多个协议继承现在看起来像这样:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol {

}

protocol<>在Swift 3.0中使用构造:

使用该protocol<>构造的合成已被弃用。现在更早的protocol<SomeProtocol, SomeOtherProtocol>样子是这样的:

func someFunc<T:SomeProtocol & SomeOtherProtocol>(arg: T) {

}

参考文献。

有关更改的更多信息,请where参见:https : //github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

并且,有关协议<>构造的更多更改位于:https : //github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md


13

Swift 3提供多达3种不同的方法来声明您的函数。

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1.使用&运算符

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    /* ... */
}

2. using where子句

func someFunc<T>(arg: T) where T: SomeProtocol, T: SomeOtherProtocol {
    /* ... */
}

3. using where子句和&运算符

func someFunc<T>(arg: T) where T: SomeProtocol & SomeOtherProtocol {
    /* ... */        
}

另请注意,可以使用typealias来缩短函数声明。

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.