【问题标题】:Generics call with Type T in Swift在 Swift 中使用 Type T 进行泛型调用
【发布时间】:2014-07-02 22:37:31
【问题描述】:

在我的应用程序中,我想创建一个泛型方法,该方法根据给定类型 T 创建一个对象数组。

我创建了以下函数:

func getArray<T : ROJSONObject>(key:String) -> T[] {
    var elements = T[]()

    for jsonValue in getValue(key).array! {
        var element = T()

        element.jsonData = jsonValue
        elements.append(element)
    }

    return elements
}

现在我想在调用方法时传递类型,因此它确实知道应该在内部创建哪种类型。我认为在 Java 和 C# 中你可以使用这样的方法:

object.getArray<Document>("key")

当我这样称呼它时,我总是得到错误:

Cannot explicitly specialize a generic function

所以我的解决方法是定义一个包含类型 T 实例的附加参数,以便它自动检测类型:

func getArray<T : ROJSONObject>(key:String, type:T) -> T[] {
    var elements = T[]()

    for jsonValue in getValue(key).array! {
        var element = T()

        element.jsonData = jsonValue
        elements.append(element)
    }

    return elements
}

真的没有其他方法可以在不传递未使用的实例的情况下获得该行为吗?还是我误解了什么?

进一步测试

在回答 jtbandes 之后,我进行了更多测试。我试图通过在调用中添加as 来强制类型。

class Person {

    init() { }

    func getWorkingHours() -> Float {
        return 40.0
    }
}

class Boss : Person {
    override func getWorkingHours() -> Float {
        println(100.0)
        return 100.0
    }
}

class Worker : Person {
    override func getWorkingHours() -> Float {
        println(42.0)
        return 42.0
    }
}

func getWorkingHours<T : Person>() -> T {
    var person = T()
    person.getWorkingHours()

    return person
}

var worker:Worker = getWorkingHours() as Worker
var boss:Boss = getWorkingHours() as Boss
worker.getWorkingHours() // prints out 40.0 instead of 42.0
boss.getWorkingHours() // prints out 40.0 instead of 100.0

所以不知何故,即使我用as 关键字指定了类型,该类型也始终是基本类型。我知道这个例子没有多大意义,但它只是为了测试目的..

【问题讨论】:

  • 哦,没有 Swift 类型系统被完全破坏(再一次)。此外,如果基类是从 NSObject 继承的,它“有效”。 code here
  • 答案很简单。您必须以某种方式指定类型,以便将其作为 object.getArray("key") as [Document]

标签: generics swift type-inference


【解决方案1】:

我通过借用 swift 运行时函数 unsafeBitCast 来解决这个问题。

它被声明为func unsafeBitCast&lt;T, U&gt;(x: T, _: U.Type) -&gt; U,你可以调用它为unsafeBitCast(value, MyType)。

应用到你的函数中

func getArray<T : ROJSONObject>(key:String, _: T.Type) -> T[] {
    // function stays the same
}

你可以这样称呼它

getArray("key", Document)

【讨论】:

  • 我认为正确的调用是 getArray("key", Document.self)
  • 仅当泛型函数接收多个参数时。如果它只期望一个参数,它允许传递 Document
  • 这太奇怪了,您需要像这样将它作为参数传递。从可读性的角度来看, getArray 更有意义。不过这行得通,谢谢!
  • getArray 是 C++ 风格。不是斯威夫特。 swift 知道您应用函数的对象的类型。调用 object.getArray(key:"your key") 时,对象的类型就是要使用的对象。
【解决方案2】:

您应该能够使用as 关键字来指示类型:object.getArray("key") as Document[]。

【讨论】:

  • 感谢您的回答。我编辑了我的问题并尝试了您的解决方案。但不知何故,它总是停留在基类中,并且不会在内部通过关键字“as”创建指定类的对象。
  • 我现在不能测试更多,但你可以考虑filing a bug。
  • 我提交了一个错误报告。让我们看看苹果的反应是什么。当我得到答案时会在这里发布。
  • 通常需要很长时间才能回复,但提交报告仍然是个好主意:)
【解决方案3】:

我认为这是一个错误。

您可以通过将类设为NSObject 的子类或使用@required 标记基类的构造函数来解决此问题

import Cocoa

class A : NSObject {
    init() { }
}
class B : A {}
class C : A {}

func Create<T:NSObject> () -> T {
    return T()
}

println(Create() as A)
println(Create() as B)
println(Create() as C)

//<_TtC11lldb_expr_01A: 0x7f85ab717bc0>
//<_TtC11lldb_expr_01B: 0x7f85ab451e00>
//<_TtC11lldb_expr_01C: 0x7f85ab509160>

class D {
    @required init() { } 
}

class E : D {
    init() { }
}

class F : D {
    init() { }
}

func Create2<T:D> () -> T {
    return T()
}

println(Create2() as D)
println(Create2() as E)
println(Create2() as F)

//C11lldb_expr_01D (has 0 children)
//C11lldb_expr_01E (has 1 child)
//C11lldb_expr_01F (has 1 child)

不知道为什么@required 解决了这个问题。但是this is the reference

必填

将此属性应用于指定的或方便的 类的初始值设定项,以指示每个子类必须实现 那个初始化器。

必需的指定初始化器必须显式实现。 所需的便利初始化程序可以显式实现 或在子类直接实现所有 超类的指定初始化器(或当子类覆盖时) 指定的初始化器和便利初始化器)。

【讨论】:

  • 谢谢,有了@required,我可以解决我的问题!
【解决方案4】:

您需要创建初始化器required,然后添加let realType = T.self 行并将T() 替换为realType()。

class Person {
    required init() { }

    func getWorkingHours() -> Float {
        return 40.0
    }
}

class Boss : Person {
    override func getWorkingHours() -> Float {
        println(100.0)
        return 100.0
    }
}

class Worker : Person {
    override func getWorkingHours() -> Float {
        println(42.0)
        return 42.0
    }
}

func getWorkingHours<T : Person>() -> T {
    let realType = T.self
    var person = realType()
    person.getWorkingHours()

    return person
}

【讨论】:

    【解决方案5】:

    回答你问题的第一部分

    你不需要在方法中添加额外的参数来指定T的类型,因为你已经将[T]作为返回类型

    所以你替换这一行

    object.getArray<Document>("key")
    

    有了这个

    object.getArray("key") as [Document]
    

    这样您就有机会将类型指定为T。

    【讨论】:

      【解决方案6】:

      不要这样称呼它

      object.getArray<Document>("key")
      

      相反,省略“”部分,如下所示:

      object.getArray("key")
      

      为我解决了这个问题。

      【讨论】:

        【解决方案7】:

        问题中代码的工作版本(第二部分)

        • Init 是必需的,因为函数返回一个通用的 person 类型 ( newPerson(as person:T) -&gt; T)

        • 我已将类方法 func getWorkingHours() 替换为 var workingHours { get }.. 它的工作原理相同,而且以更“迅速”的方式。

        •getWorkingHours() 返回小时数,正如名称所示,而不是人..

        class Person {
            required init() {}
            var workingHours: Float { get { return 40 } }
        }
        
        class Boss : Person {
            override var workingHours: Float { get { return 100 } }
        }
        
        class Worker : Person {
            override var workingHours: Float { get { return 42 } }
        }
        
        func getWorkingHours<T : Person>(_ person: T) -> Float {
            return person.workingHours
        }
        
        func newPerson<T : Person>(as person: T) -> T {
            return T()
        }
        
        // -----------------------------------------------
        
        let worker = Worker()
        let boss = Boss()
        
        print( getWorkingHours(worker) ) // prints 42.0
        print( getWorkingHours(boss) ) // prints 100.0
        
        let person = newPersonOfSameClass(as: worker)
        
        print( getWorkingHours(person) ) // prints 42.0
        

        希望对您有所帮助.. 此代码已准备好在 Playground 中复制/粘贴。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-07
          • 2013-01-28
          • 1970-01-01
          • 2017-08-14
          相关资源
          最近更新 更多