可以使用条件来确定泛型的类型吗?

2024-01-02

在讨论这个问题之前,我将首先解释我想要做什么以及我是如何陷入困境的。


作为我自己的学习练习,我解决了一些已经在 Objective-C 中解决的问题,看看如何使用 Swift 以不同的方式解决它们。我遇到的具体情况是一个小片段,它捕获更改之前和之后的值,并在两者之间进行插值以创建动画的关键帧。

为此我有一个目标Capture包含对象的属性、关键路径和两个id之前和之后的值的属性。后来,当对捕获的值进行插值时,我确保可以通过将每个值包装在一个Value使用类簇根据其包装的值的类型返回适当的类的类,或者nil对于不支持的类型。

这是可行的,我也可以按照相同的模式使其在 Swift 中工作,但感觉不像 Swift。


什么有效

我没有将捕获的值包装为启用插值的方式,而是创建了一个Mixable当类型支持必要的基本算术时,类型可以遵循的协议并使用协议扩展:

protocol SimpleArithmeticType {
    func +(lhs: Self, right: Self) -> Self
    func *(lhs: Self, amount: Double) -> Self
}

protocol Mixable {
    func mix(with other: Self, by amount: Double) -> Self
}

extension Mixable where Self: SimpleArithmeticType {
    func mix(with other: Self, by amount: Double) -> Self {
        return self * (1.0 - amount) + other * amount
    }
}

这部分工作得非常好,并强制执行同质混合(类型只能与其自己的类型混合),而 Objective-C 实现中并未强制执行。

我被困住的地方

下一个逻辑步骤,这就是我陷入困境的地方,似乎是让每个 Capture 实例(现在是一个结构)保存两个相同可混合类型的变量,而不是两个AnyObject。我还将初始化参数从对象和关键路径更改为返回对象的闭包()->T

struct Capture<T: Mixable> {
    typealias Evaluation = () -> T
    let eval: Evaluation

    let before: T
    var after: T {
        return eval()
    }

    init(eval: Evaluation) {
        self.eval = eval
        self.before = eval()
    }
}

当可以推断类型时,这有效,例如:

let captureInt = Capture {
    return 3.0
}
// > Capture<Double>

但不使用键值编码,它返回 AnyObject:\

let captureAnyObject = Capture {
    return myObject.valueForKeyPath("opacity")!
}

错误:无法使用类型为“(() -> _)”的参数列表调用类型“Capture”的初始值设定项

AnyObject不符合Mixable协议,所以我可以理解为什么这不起作用。但我可以检查对象到底是什么类型,并且由于我只涵盖了少数可混合类型,因此我认为我可以涵盖所有情况并返回正确的 Capture 类型。太见了if这甚至可以工作我做了一个更简单的例子

一个更简单的例子

struct Foo<T> {
    let x: T
    init(eval: ()->T) {
        x = eval()
    }
}

当类型推断得到保证时,它会起作用:

let fooInt = Foo {
    return 3
}
// > Foo<Int>
let fooDouble = Foo {
    return 3.0
}
// > Foo<Double>

但当闭包可以返回不同类型时则不然

let condition = true
let foo = Foo {
    if condition {
        return 3
    } else {
        return 3.0
    }
}

错误:无法使用类型为“(() -> _)”的参数列表调用类型“Foo”的初始值设定项

我什至无法自己定义这样的闭包。

let condition = true // as simple as it could be
let evaluation = {
    if condition {
        return 3
    } else {
        return 3.0
    }
}

错误:无法推断当前上下文中的闭包类型

我的问题

这是可以做的事情吗?可以使用条件来确定泛型的类型吗?或者是否有另一种方法来保存相同类型的两个变量,其中类型是根据条件决定的?


Edit

我真正想要的是:

  1. 捕获更改之前和之后的值并保存该对(旧+新)以供以后使用(同类对的异构集合)。
  2. 遍历所有收集的值并删除无法插值的值(除非此步骤可以与收集步骤集成)
  3. 单独插入每个同质对(混合旧+新)。

但在解决这个问题时,这个方向似乎是一个死胡同。我将不得不后退几步并尝试不同的方法(如果我再次陷入困境,可能会问一个不同的问题)。


正如所讨论的Twitter https://twitter.com/davidronnqvist/status/628231101379686400,类型必须在编译时已知。不过,对于问题末尾的简单示例,您可以显式键入

let evaluation: Foo<Double> = { ... }

它会起作用的。

所以在这种情况下Capture and valueForKeyPath:恕我直言,您应该将值转换(安全或强制转换)到Mixable输入您期望的值,它应该可以正常工作。毕竟,我不确定valueForKeyPath:应该根据条件返回不同的类型。

您希望返回两种完全不同类型的确切情况是什么(不能像简单情况那样隐式转换)Int and Double上面)在同一个评估闭包中?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可以使用条件来确定泛型的类型吗? 的相关文章

随机推荐