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

标签 swift generics swift2

在回答问题之前,我将首先解释我正在尝试做什么以及我是如何到达卡住的地方的。


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

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

这行得通,而且我能够按照相同的模式使其在 Swift 中工作,但感觉不像 Swift。


有效的方法

我没有将捕获的值作为启用插值的方式进行包装,而是创建了一个类型可以符合的 Mixable 协议(protocol),并在类型支持必要的基本算法时使用协议(protocol)扩展:

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")!
}

error: cannot invoke initializer for type 'Capture' with an argument list of type '(() -> _)'

AnyObject 不符合 Mixable 协议(protocol),所以我可以理解为什么这不起作用。但我可以检查对象的真正类型,并且由于我只涵盖了少数可混合类型,所以我虽然可以涵盖所有情况并返回正确的 Capture 类型。看看如果这甚至可以工作我做了一个更简单的例子

一个更简单的例子

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
    }
}

error: cannot invoke initializer for type 'Foo' with an argument list of type '(() -> _)'

我什至不能自己定义这样的闭包。

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

error: unable to infer closure type in the current context

我的问题

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


编辑

我真正想要的是:

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

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

最佳答案

正如在 Twitter 上讨论的那样,类型必须在编译时已知。然而,对于问题末尾的简单示例,您可以明确键入

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

它会起作用的。

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

您想在同一个计算闭包中返回 2 个完全不同的类型(不能像上面的 IntDouble 的简单情况那样隐式转换)的确切情况是什么?

关于swift - 可以使用条件来确定泛型的类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31814457/

相关文章:

ios - swift 2.0 : How do I specify which Annotation is pressed when having more then one Annotation? 市场

swift - swift 四舍五入到最接近的小数点后 .5

ios - 带有自定义单元格的可展开/可折叠 TableView 常见问题解答

java - 如何使方法返回类型通用?

swift - 苹果自己的 Swift 文档关于动态类型与编译时类型似乎是错误的

swift - 自定义注释调用

c# - EF 通用存储库多重包含和按调用排序

c# - 定义嵌套的无界泛型类型

ios - 如何更改 Swift header ("ProductModuleName-Swift.h") 产品名称作为产品名称动态变化

ios编程: Keeping user configuration in memory vs storage