有没有办法将`Self`限制为泛型? [英] Is there a way to constrain `Self` to a generic type?

查看:96
本文介绍了有没有办法将`Self`限制为泛型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

https://www.raywenderlich.com/148448/introducing-protocol面向程序设计

protocol Bird {
    var name: String { get }
    var canFly: Bool { get }
    func doSomething()
}

protocol Flyable {
    var airspeedVelocity: Double { get }
}

extension Bird {
    // Flyable birds can fly!
    var canFly: Bool { return self is Flyable }
    func doSomething() {
        print("default Bird: \(name)")
    }
}

class FlappyBird: Bird, Flyable {
    let name: String
    let canFly = true
    var airspeedVelocity: Double = 5.0

    init(name: String) {
        self.name = name
    }
}

class Penguin: Bird {
    let name: String
    let canFly = false

    init(name: String) {
        self.name = name
    }
}

class Owl<T> : Bird {
    let name: String
    let power: T

    init(name: String, power: T) {
        self.name = name
        self.power = power
    }
}

extension Bird where Self: FlappyBird {
    func doSomething() {
        print("FlappyBird: \(name)")
    }
}

extension Bird where Self: Owl<String> {
    func doSomething() {
        print("Owl<String>: \(name)")
    }
}

    let fb = FlappyBird(name:"PAK")
    let penguin = Penguin(name:"Mr. Pickle")
    let nightOwl = Owl<String>(name:"Night Owl", power:"Who")
    let dayOwl = Owl<Int>(name:"Day Owl", power: 50)

    let birds: [Bird] = [fb, penguin, nightOwl, dayOwl]

    birdloop: for bird in birds {
        bird.doSomething()
    }

我得到的输出是:

FlappyBird: PAK
default Bird: Mr. Pickle
default Bird: Night Owl
default Bird: Day Owl

自第一个结果以来,第一个结果起作用

The first result works as expected since

extension Bird where Self: FlappyBird {
    func doSomething() {
        print("FlappyBird: \(name)")
    }
}

第二个结果可以正常工作,因为它调用了默认协议扩展名:

The second result works as expected since it calls the default protocol extension:

extension Bird {
        // Flyable birds can fly!
        var canFly: Bool { return self is Flyable }
        func doSomething() {
            print("default Bird: \(name)")
        }
    }

我希望打印的第三个结果

The third result I would expect to print

Owl<String>: Night Owl

,因为nightOwlOwl<String>类型.而是调用了默认的协议扩展名:

since nightOwl is of type Owl<String>. But instead it calls the default protocol extension:

default Bird: Night Owl

有什么理由

extension Bird where Self: FlappyBird {
  func doSomething() {
    print("default Bird: \(name)")
  }
}

被称为FlappyBird类型,但

extension Bird where Self: Owl<String> {
  func doSomething() {
    print("Owl<String>: \(name)")
  }
}

不是不是类型的请求?

推荐答案

对于通用类型Owl<T>,允许使用约束where Self: Owl<String>,但它仅在可获得具体类型信息的上下文中有效.

For the generic type Owl<T> you are allowed to have the constraint where Self: Owl<String>, but it will only work in contexts where the concrete type information is available.

要弄清楚正在发生的事情,请考虑以下问题:

To make it clear what is happening, consider this:

let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"

与此相反:

let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"

当Swift为Owl<T>FlappyBird类型创建协议见证表时,由于Owl是通用的,因此它必须对每个表采取不同的操作.如果它没有具体的类型信息,即在呼叫站点上的Owl<String>,则它必须对Owl<T>使用默认的实现.当您将猫头鹰插入到类型为[Bird]的数组中时,就会发生类型信息的丢失".

When Swift creates the protocol witness tables for the types Owl<T> and FlappyBird, it has to act differently for each one because Owl is generic. If it doesn't have the concrete type information, i.e. Owl<String> at the call site, it must use the default implementation for Owl<T>. This "loss" of type information is happening when you are inserting the owls into the array of type [Bird].

FlappyBird的情况下,由于只有一种可能的实现方式(因为它不是通用的),因此编译器将生成带有预期"方法引用的见证表,即print("FlappyBird: \(name)").由于FlappyBird不是通用的,因此它的见证表不需要对doSomething()的不受约束的默认实现的任何引用,因此即使缺少具体的类型信息,也可以正确地调用受约束的实现.

In the case of FlappyBird, since there is only one possible implementation (since it's not generic), the compiler produces a witness table with the "expected" method reference, which is print("FlappyBird: \(name)"). Since FlappyBird is not generic, its witness table doesn't need any reference to the unconstrained default implementation of doSomething() and can therefore correctly call the the constrained implementation even when the concrete type information is missing.

要明确表明编译器需要"具有泛型类型的后备行为,可以从Owl<T>中删除Bird一致性,并尝试仅依赖于受约束的默认实现.这将导致编译错误,并带有一个与Swift一样的高度误导性错误.

To make it clear that the compiler "needs" to have the fall back behavior for a generic type, you can remove the Bird conformance from Owl<T> and try to rely solely on the constrained default implementation. This will result in a compilation error with an error that is, as usual with Swift, highly misleading.

猫头鹰"类型的值没有成员"doSomething"

Value of type 'Owl' has no member 'doSomething'

基本上,似乎无法建立见证表,因为它需要存在一个对Owl上的所有T类型都适用的实现.

Basically, it seems the witness table can't be built because it requires the existence of an implementation that will work for all types T on Owl.

参考

  1. https://forums.swift.org/t/swifts-method -dispatch/7228
  2. https://developer.apple.com/videos/play/wwdc2016/416/
  1. https://forums.swift.org/t/swifts-method-dispatch/7228
  2. https://developer.apple.com/videos/play/wwdc2016/416/

这篇关于有没有办法将`Self`限制为泛型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆