4

我遇到了一个问题,我有一些协议:

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
    }

第一种形式给出了“相同类型的要求使通用参数'S'和'T'等效”错误(这正是我想要做的,所以不知道为什么它是一个错误)。第二种形式给了我一个“继承自非协议、非类类型'S'”。

关于如何使其工作或在 Swift 中更好的设计模式的任何想法?

更新:正如@luk2302 指出的,我忘了Foo遵守Baz协议

4

2 回答 2

2

@luk2302 在评论中暗示了大部分内容,但只是为了让未来的搜索者明白这一点。

protocol Baz {
    func bar<T>(input:T)
}

这个协议几乎可以肯定是没有用的。它实际上与以下协议相同(也几乎完全没用):

protocol Baz {
    func bar(input:Any)
}

您很可能是指(并暗示您的意思):

protocol Baz {
    typealias T
    func bar(input: T)
}

正如您所注意到的,这使协议成为 PAT(具有关联类型的协议),这意味着您不能将其直接放入集合中。正如您所注意到的,如果您真的需要它们的集合,通常的解决方案是类型橡皮擦。如果 Swift 会自动为您编写橡皮擦,那就太好了,它将来可能会这样做,并且会消除问题。也就是说,虽然有点乏味,但书写类型的橡皮擦非常简单。

现在,虽然您不能将 PAT 直接放入集合中,但可以将一般约束的 PAT 放入集合中。所以只要把集合包装成约束T的类型,还是没问题的。

如果这些变得复杂,约束代码可能会变得乏味且非常重复。然而,这可以通过许多技术来改善。

具有静态方法的通用结构可用于避免重复提供对自由函数的约束。

该协议可以转换为通用结构(这将类型擦除器形式化为主要类型,而不是“根据需要”)。

在许多情况下,协议可以用函数代替。例如,鉴于此:

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]

这对于单方法协议特别强大。

协议、泛型和函数的混合比试图把所有东西都强加到协议盒中要强大得多,至少在协议添加更多缺失的特性来实现它们的承诺之前是这样。

(针对特定问题提供特定建议会更容易。没有一个答案可以解决所有问题。)

于 2015-12-27T21:35:41.427 回答
0

已编辑(解决方法“...一个错误,因为编译器不知道 S 和 T 是同一类型。”

首先:除了 Rob 出色的答案之外,这只是一个单独的注释(也许是为了赎回我之前的答案,最终我自己追逐自己的尾巴来计算大量的冗余代码)。

以下解决方法可能会让您的实现protocol Foo .../class Bas : Foo ...模仿您最初要求的行为,因为类方法bar(...)将知道泛型是否实际上是相同的类型,S而Foo 在属于的情况下仍然T符合协议与 相同的类型。ST

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

此处的AnyandAnyObject漏洞可以通过创建一个虚拟类型约束来弥补,您可以将所有类型(您希望使用泛型)扩展到,但不扩展Anyand 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

然而,这完全排除AnyAnyObject泛型(不仅用于“漏洞铸造”),这可能不是一种受追捧的行为。

于 2015-12-27T21:03:05.130 回答