当前位置: 首页 > 面试题库 >

使用协议中定义的默认参数实现功能

樊杰
2023-03-14
问题内容

Swift协议可以通过向函数和计算属性添加扩展来为其提供默认实现。我已经做了很多次。据我了解, 默认实现仅用作“后备”
:当类型符合协议但不提供其自己的实现时,将 执行默认 实现。

至少这就是我阅读《
Swift编程语言
指南的方式:

如果符合类型提供了自己的所需方法或属性的实现,则将使用该实现而不是扩展提供的实现。

现在,我遇到了这样的情况:我的实现某种协议的自定义类型 确实 为特定功能提供了一种实现,但是并未执行-而是执行协议扩展中定义的实现。

例如 ,我定义了一个协议Movable,该协议具有一个功能move(to:)和一个扩展,为该功能提供默认实现:

protocol Movable {

    func move(to point: CGPoint)

}

extension Movable {

    func move(to point: CGPoint = CGPoint(x: 0, y: 0)) {
        print("Moving to origin: \(point)")
    }

}

接下来,我定义一个Car符合Movable但提供其自己的move(to:)函数实现的类:

class Car: Movable {

    func move(to point: CGPoint = CGPoint(x: 0, y: 0)) {
        print("Moving to point: \(point)")
    }

}

现在,我创建一个新文件,Car并将其转换为Movable

let castedCar = Car() as Movable

根据是否为可选参数传递值,point我观察到两种不同的行为:

  1. *为可选参数 *传递点时

Car的实现被称为

    castedCar.move(to: CGPoint(x: 20, y: 10))

输出:

移至点:(20.0,10.0)

  1. 当我调用move()功能, 而无需为可选参数提供一个值Car的执行将被忽略,

Movable协议的默认实现是不是

    castedCar.move()

输出:

移至原点:(0.0,0.0)


问题答案:

这是由于该呼叫

castedCar.move(to: CGPoint(x: 20, y: 10))

能够满足协议要求func move(to point: CGPoint)-因此,该调用将通过协议见证表(协议类型的值实现多态性的机制)动态分派到,从而允许Car调用的实现。

但是,电话

castedCar.move()

不会 匹配协议的要求func move(to point: CGPoint)。因此,它不会通过协议见证表(仅包含协议 要求的
方法条目)进行调度。相反,如castedCar键入为Movable,编译器将不得不依赖静态调度。因此,将调用协议扩展中的实现。

默认参数值只是函数的静态功能-编译器实际上只会发出函数的单个重载(一个带有 所有
参数的重载)。尝试通过排除具有默认值的参数之一来应用函数,将触发编译器插入该默认参数值的评估值(因为它可能不是恒定的),然后在调用位置插入该值。

因此,具有默认参数值的函数不能很好地与动态分配配合使用。您还可以使用具有默认参数值的类覆盖方法获得意想不到的结果-
例如,参见此错误报告。

获得默认参数值所需的动态调度的一种方法是static在协议中定义属性要求,以及move()协议扩展中的过载(仅适用于此)move(to:)

protocol Moveable {
    static var defaultMoveToPoint: CGPoint { get }
    func move(to point: CGPoint)
}

extension Moveable {

    static var defaultMoveToPoint: CGPoint {
        return .zero
    }

    // Apply move(to:) with our given defined default. Because defaultMoveToPoint is a 
    // protocol requirement, it can be dynamically dispatched to.
    func move() {
        move(to: type(of: self).defaultMoveToPoint)
    }

    func move(to point: CGPoint) {
        print("Moving to origin: \(point)")
    }
}

class Car: Moveable {

    static let defaultMoveToPoint = CGPoint(x: 1, y: 2)

    func move(to point: CGPoint) {
        print("Moving to point: \(point)")
    }

}

let castedCar: Moveable = Car()
castedCar.move(to: CGPoint(x: 20, y: 10)) // Moving to point: (20.0, 10.0)
castedCar.move() // Moving to point: (1.0, 2.0)

因为defaultMoveToPoint现在是协议要求–可以将其动态分配给您,从而为您提供所需的行为。

作为附录,请注意,我们调用defaultMoveToPointtype(of: self),而不是Self。这将为我们提供实例动态元
类型值,而不是方法调用时的静态元类型值,从而确保defaultMoveToPoint正确分派。但是,如果move()调用的任何对象的静态类型(Moveable本身除外)就足够了,则可以使用Self



 类似资料:
  • 问题内容: 我想知道是否有可能实现这样的目标。 我有一个这样的游乐场: 我可以在中提供默认实现,但是如果需要默认实现中的所有内容以及其他内容,该怎么办? 它在某种程度上类似于es中的调用方法,可以满足实现每个属性等的要求。但是我看不到用实现相同的可能性。 问题答案: 我不知道您是否还在寻找答案,但是要做的方法是从协议定义中删除函数,将对象转换为对象,然后在其上调用方法: 由于某种原因,它仅在函数未

  • 问题内容: 考虑以下游乐场: 我不知道为什么然后返回-它们应该返回,因为应该动态调度(如协议中声明的那样)。 如果我更改为以下形式: 然后所有三个呼叫将按预期返回。 我觉得这是Swift编译器中的错误,我在Xcode 7.3.1和8.0-beta3中进行了检查,这两种行为都可以重现。 这实际上是预期的行为吗? 问题答案: 您这里涉及到一些规则。 有时使用静态调度(在这种情况下,我们必须查看var

  • 问题 你想定义一个函数或者方法,它的一个或多个参数是可选的并且有一个默认值。 解决方案 定义一个有可选参数的函数是非常简单的,直接在函数定义中给参数指定一个默认值,并放到参数列表最后就行了。例如: def spam(a, b=42): print(a, b) spam(1) # Ok. a=1, b=42 spam(1, 2) # Ok. a=1, b=2 如果默认参数是一个可修改的容器

  • 问题内容: 我正在尝试将我的爱好项目之一移植到linux。最好使用Mono,因为它是用C#编写的。但是我也在研究Python。 该应用程序的功能之一是它需要与自定义协议相关联,因此,当用户单击应用程序网站上的链接时,将调用该应用程序: 像这样,这个和这个的定制协议。 在linux / unix系统中怎么做?我可以像Windows中那样关联系统范围的处理程序吗?还是需要依赖于浏览器? 在Google

  • 本文向大家介绍Python如何定义有默认参数的函数,包括了Python如何定义有默认参数的函数的使用技巧和注意事项,需要的朋友参考一下 问题 你想定义一个函数或者方法,它的一个或多个参数是可选的并且有一个默认值。 解决方案 定义一个有可选参数的函数是非常简单的,直接在函数定义中给参数指定一个默认值,并放到参数列表最后就行了。例如: 如果默认参数是一个可修改的容器比如一个列表、集合或者字典,可以使用

  • 问题内容: 我对PHP函数的默认值感到困惑。说我有一个这样的功能: 如果我想为$ x使用默认参数并为$ y设置不同的参数怎么办? 我一直在尝试不同的方法,但我变得更加困惑。例如,我尝试了以下两种方法: 但是这两个都不会为$ x产生适当的默认参数。我也试图通过变量名来设置它。 我完全希望这样的事情能奏效。但这根本不符合我的预期。似乎不管我做什么,每次调用该函数时,无论如何我都必须最终键入默认参数。而