我遇到了一个问题,我有一些协议:
protocol Baz {
func bar<T>(input:T)
}
函数bar
是通用的,因为我不希望协议本身具有Self
(它需要在集合中可用)。我有一个定义为:
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
value = input
}
}
这会产生错误,因为编译器不知道S
和T
是同一类型。理想情况下,我应该能够写出类似的内容:
func bar<T where T==S>(input:T) {
value = input
}
或
func bar<T:S>(input:T) {
value = input
}
第一种形式给出了#34;相同类型的要求使得通用参数&#39; S&#39;和&#39; T&#39;等效&#34;错误(这正是我试图做的,所以不确定为什么它是一个错误)。第二种形式给了我一个&#34;继承自非协议,非类型&#39;&#39;&#34;&#34;
关于如何使其工作的任何想法,或Swift中更好的设计模式?
更新:正如@ luk2302指出的那样,我忘了让Foo
遵守Baz
协议
答案 0 :(得分:2)
@ luk2302在评论中暗示了很多内容,但只是为了让未来的搜索者明白。
protocol Baz {
func bar<T>(input:T)
}
这个协议几乎肯定没用。它实际上与以下协议相同(也几乎完全没用):
protocol Baz {
func bar(input:Any)
}
你很可能意味着(并暗示你的意思):
protocol Baz {
typealias T
func bar(input: T)
}
如您所知,这使协议成为PAT(具有关联类型的协议),这意味着您无法将直接放入集合中。正如您所注意到的,如果您确实需要它们的集合,通常的解决方案是type eraser。如果Swift会为你自动编写橡皮擦,这将很有可能在将来做,并且可以消除这个问题。虽然说虽然有点单调乏味,但写入类型的橡皮擦非常简单。
现在,虽然您无法将PAT直接放入集合中,但您可以将通用约束的PAT放入集合中。因此,只要将集合包装为约束T的类型,它仍然没有问题。
如果这些变得复杂,约束代码可能变得乏味且非常重复。然而,这可以通过许多技术来改进。
使用静态方法的通用结构可以避免重复提供对自由函数的约束。
协议可以转换为通用结构(这将类型橡皮擦形式化为主要类型,而不是&#34;根据需要&#34;)。
在许多情况下,协议可以用函数替换。例如,鉴于此:
protocol Bar {
typealias T
func bar(input: T)
}
struct Foo : Bar {
func bar(input: Int) {}
}
你不能这样做:
let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
但你可以很容易地做到这一点,这也很好:
let bars = [(Int) -> Void] = [Foo().bar]
这对单方法协议来说特别有用。
协议,泛型和函数的混合比尝试将所有内容强制进入协议框要强大得多,至少在协议添加一些缺少的功能以实现其承诺之前。
(对特定问题提出具体建议会更容易。没有一个答案可以解决所有问题。)
答案 1 :(得分:0)
已编辑(的解决方法“......错误,因为编译器不知道S和T是同一类型。”)
首先:这只是一个单独的注释(也许是为了兑换我以前的答案,最终是我自己追逐自己的尾巴来计算大量的冗余代码)以及Robs的优秀答案。 / p>
以下解决方法可能会让您的实施protocol Foo ...
/ class Bas : Foo ...
模仿您最初要求的行为,因为类方法bar(...)
会知道泛型S
并且T
实际上是相同的类型,而在S
与T
相同类型的情况下,Foo仍然符合协议。< / p>
protocol Baz {
func bar<T>(input:T)
}
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0
// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0
此处的Any
和AnyObject
漏洞可以通过创建一个虚拟类型约束来兑换,您可以扩展所有类型(您希望使用泛型),但不能扩展{{1} }和Any
。
AnyObject
然而,这完全排除了通用中的protocol NotAnyType {}
extension Int : NotAnyType {}
extension Double : NotAnyType {}
extension Optional : NotAnyType {}
// ...
protocol Baz {
func bar<T: NotAnyType>(input:T)
}
class Foo<S: NotAnyType>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T: NotAnyType>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
// ...
// no longer a loophole
let myAny : Any = 3.0
a.bar(myAny) // compile time error
let myAnyObject : AnyObject = 3.0
a.bar(myAnyObject) // compile time error
和Any
(不仅仅是“漏洞投射”),这可能不是一个受追捧的行为。