【问题标题】:Swift: constrain a generic parameter for use in a more constrained generic functionSwift:约束一个泛型参数以在更受约束的泛型函数中使用
【发布时间】:2018-08-21 17:36:32
【问题描述】:

我正在实现一个需要符合如下协议的类:

protocol P {
    func magic<T>(_ thing: Thing, as type: T.Type) -> T
}

此协议由第三方代码提供,这意味着我无法以任何方式更改它以解决此问题。

现在,我有一个通用函数

func magicForObject<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T

我想在magic 的实现中调用它,只是为了输入things,它们实际上是对象。 也就是说,我想做这样的事情:

func magic<T>(_ thing: Thing, as type: T.Type) -> T {
    if T.self is AnyClass {
        return magicForObject(thing, as: type)
    }
    else {
        // do something else
    }
}

但我找不到任何方法来完成这项工作。上面的代码显然不能编译,像

这样的东西也不能编译
if let T_ = T.self as? AnyClass { ... }

因为T_ 只是一个普通变量,而不是泛型参数(可能是编译时)。

我也试过这样做:

func magic<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T { ... }
func magic<T>(_ thing: Thing, as type: T.Type) -> T { ... }

并分别实现两者。如果直接在对象上调用此函数,则会正确调用受约束的AnyObject,但当对象转换为协议类型P 时,不会,在这种情况下,总是使用第二个。

这种情况似乎受到了无可救药的限制,但有没有我没有想到的解决方法?

更新

目前看来,这在 Swift 中是不可能的。我在 Swift 论坛中创建了 post pitching the idea;如果这也是您需要的,请随时加入。

【问题讨论】:

  • 所以你要做的是检查magic 中的泛型参数是否是class 然后你想对该类做一些事情并从magicForObject 返回它而不是structs。我理解正确吗?您还为协议提供自己的默认实现吗?
  • @Serj 是的,如果T magic 是一个类类型,我希望能够获得某种T_: AnyObject,这样我就可以调用magicForObject,否则做一些事情else 用于结构等。这不是P 的默认实现,我只是有一个需要符合P 的类。

标签: swift function generics


【解决方案1】:

你的例子有点难以处理,所以我不得不做很多假设,但我想这应该可以满足你的要求。

从你所说的你有一个给定的协议 P:

protocol P {
    func magic<T>(_ thing: Thing, as type: T.Type) -> T
}

让我们给 P 一个你需要它做的默认实现:

extension P {
    // Implementation for magic where T is a class
    func magic<T>(_ thing: Thing, as type: T.Type) -> T where T: AnyObject {
        print("AnyObject Called")
        return Test2() as! T
    }

    // Implementation for magic where T is a struct
    func magic<T>(_ thing: Thing, as type: T.Type) -> T {
        print("Struct Called")
        return Test() as! T
    }
}

你有一个符合 P 的类

class Test2: P {

}

假设你有这个 Thing 对象和一个我们想要传递的结构,看看我们是否有正确的结果:

class Thing {

}

struct Test {

}

现在让我们测试我们是否在Test2 上调用magic,它是否会根据type 传递给magic 的内容调用正确的函数

let test = Test()
let test2 = Test2()

// T is Test2 so its a class
test2.magic(Thing(), as: Test2.self)
// T is Test so its a struct
test2.magic(Thing(), as: Test.self)

打印输出调用

AnyObject Called
Struct Called

看来您可以为structs 做点什么,为classes 做另一件事

【讨论】:

  • 我试过这个并得到了相同的结果。但是现在,试着写let test2 = Test2() as P,你就不会再得到这个结果了……
  • 问题是 P 在任何实现它的类中的行为总是相同的吗?
  • 我不确定我理解你的意思。这种方法的问题在于,当您按其协议类型使用Test2 时,不再调用正确的magic。
  • 我的意思是你可以扩展P并实现magic,就像我为Test2所做的那样,事情应该会再次起作用,但这意味着任何实现P的类或结构都会有该默认实现,但可以肯定的是,您可以覆盖该实现。
  • 这行得通,但不幸的是,就我而言,我不能只为P 添加一个默认实现,所以我想我还是有点卡住了......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多