【问题标题】:Why do we need a generic here? Isn't the protocol enough?为什么我们在这里需要一个泛型?协议还不够吗?
【发布时间】:2017-06-07 00:12:42
【问题描述】:

我在网上找到了以下关于将泛型与协议一起使用的示例,但是我不明白为什么我们根本需要泛型,而我们只需要使用协议。

我们定义一个协议:

protocol Healthy {
    mutating func setAlive(status: Bool)
    var health: Int { get }
}

然后是一个使用通用协议的函数

func check<T:Healthy>(inout object: T) {
    if (object.health <= 0) {
        object.setAlive(false)
    }
}

我已将代码更改如下,一切正常。

func check( object: inout Healthy) {
    if (object.health <= 0) {
        object.setAlive(status: false)
    }
}

还是不行?

我能想到在那里使用泛型的唯一原因,如果它是一个协议具有关联类型并且它不能用作实例。

【问题讨论】:

    标签: swift generics swift-protocols


    【解决方案1】:

    他们表达不同的东西。与

    func check(object: inout Healthy) {
    

    object 参数可以是任何符合Healthy 的实例。因此,您可以这样做:

    protocol Healthy {}
    
    struct Foo : Healthy {}
    struct Bar : Healthy {}
    
    func check(object: inout Healthy) {
        object = Bar()
    }
    
    var h: Healthy = Foo()
    check(object: &h)
    print(h) // Bar()
    

    我们调用了check(object:) 并将h(它包含一个Foo 实例)作为inout 参数传递,但最终h 包含一个Bar 实例。

    您会注意到,这意味着我们不能简单地使用具体类型的inout 参数调用check(object:)。以下内容无法编译:

    var h = Foo()
    
    // compiler error: Cannot pass immutable value as inout argument: 
    // implicit conversion from 'Foo' to 'Healthy' requires a temporary
    check(object: &h)
    

    因为check(object:) 可以将一个任意符合Healthy 的实例分配给object 参数,而该参数不能分配给Foo 变量。

    然而,与

    func check<T : Healthy>(object: inout T) {
    

    object 参数是符合Healthy 的单个特定 具体类型(并且该类型在调用站点得到满足)。您不能只为其分配符合 Healthy 的任意实例,因为它可能与作为 inout 参数传递的变量类型不兼容。

    因此,现在您可以使用具体类型的 inout 参数调用它。我们现在可以说:

    protocol Healthy {
        var alive: Bool { get set }
    }
    
    struct Foo : Healthy {
        var alive: Bool
    }
    struct Bar : Healthy {
        var alive: Bool
    }
    
    func check<T : Healthy>(object: inout T) {
    
        object.alive = false
    
        // illegal
        // object = Bar()
    }
    
    var h = Foo(alive: true)
    check(object: &h)
    

    (注意h 可以输入为Foo)

    因此,在大多数情况下,您可能希望使方法成为通用方法,而不是使用协议类型的 inout 参数,因为您可能希望处理具体类型。

    【讨论】:

    • 从您的回答中,我推断在此示例中使用泛型的主要原因是避免在函数内部进行分配(在这种情况下,考虑到 inout 参数,这在概念上是错误的)。对吗?
    • @Patrick 嗯,使用泛型的主要原因是强制 inout 参数是单个具体类型(您不能将具体类型的变量传递给inout Healthy 参数)。它不会避免函数中的赋值,您仍然可以对参数进行赋值 - 但编译器强制您分配的类型是 T 类型,而不是 任意 @987654351 @符合类型。
    • @Patrick 例如,func check&lt;T : Healthy&gt;(object: inout T, other: T) { object = other } 是合法的。分配给inout 参数的能力在概念上并没有错——这是使用inout 允许的功能。
    • 嗯哦,是的,我在 func 中添加了这两行,它编译正确。让酒吧:健康=酒吧();对象 = 条形
    • 所以它只是强制使用具体类型。虽然从实用的角度来看,在这个例子中,我看不出有什么可以改变的。
    【解决方案2】:

    因为这是使用 inout (ugh) 并且不返回值意味着不需要泛型。

    当我在这个例子中使用泛型时,如果方法签名是这样的......

    func check<T:Healthy>(object: T) -> T {
    }
    

    这将确保传入的对象类型和返回的类型是相同的类型。

    如果没有泛型,您可以传入...的实例

    struct A: Healthy {}
    

    并返回...的实例

    struct B: Healthy {}
    

    嗯...也许inout 仍然是这种情况。您可以创建另一个符合协议的结构并将object 更改为新结构的实例吗?也许……以后得检查一下。

    【讨论】:

    • 我试过了,只要声明正确,两种情况下都可以分配一个变量(在泛型的情况下为具体类型,在另一种情况下为协议)。所以,我看不出使用泛型的原因,除非你有一个约束(例如返回相同的泛型对象 - 正如你指定的那样)。
    猜你喜欢
    • 2011-10-12
    • 2010-11-10
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    • 2023-03-27
    • 2018-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多