无法显式专门化泛型函数


91

我对以下代码有疑问:

func generic1<T>(name : String){
}

func generic2<T>(name : String){
     generic1<T>(name)
}

generic1(名称),结果编译器错误“无法明确专门的通用功能”

有什么办法可以避免这个错误?我无法更改generic1函数的签名,因此应为(String)-> Void


2
当无法根据上下文推断通用类型时,在此处使用通用类型有什么意义?如果仅在内部使用通用类型,则应在函数体内指定类型。
Kirsteins,2015年

是占位类型T都在使用generic1()?您如何调用该函数,以便编译器可以推断类型?
Martin R

3
我希望可以通过某种方式调用函数,例如genetic1 <blaClass>(“ SomeString”)
Greyisf 2015年

1
泛型不仅适用于编译器可以推断上下文的情况。专门说明该功能将允许推断代码的其他部分。例如:func foo<T>() -> T { ... }使用t类型T为return 的对象做某事t。明确专门T将允许t1在被infereed var t1 = foo<T>()。我希望也可以通过这种方式调用函数。C#允许
-nacho4d

1
我也碰到了这个。如果您被迫将类型作为参数传递,则创建通用函数的意义为零。这一定是个错误吧?!
尼克

Answers:


160

我也遇到了这个问题,我找到了解决此问题的方法。

在本文中,作者有同样的问题

https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide

因此,问题似乎在于,编译器需要以某种方式推断T的类型。但是不允许简单地使用generic <type>(params ...)。

通常,编译器可以通过扫描参数类型来查找T的类型,因为在很多情况下都使用T。

就我而言,这有点不同,因为我函数的返回类型是T。就您而言,您似乎在函数中根本没有使用过T。我想您只是简化了示例代码。

所以我有以下功能

func getProperty<T>( propertyID : String ) -> T

而且例如

getProperty<Int>("countProperty")

编译器给我错误:

无法显式专门化泛型函数

因此,要给编译器提供另一种信息来源来推断T的类型,您必须显式声明要保存返回值的变量的类型。

var value : Int = getProperty("countProperty")

这样,编译器知道T必须是整数。

因此,我认为总体而言,这仅意味着如果您指定泛型函数,则必须至少在参数类型或返回类型中使用T。


2
节省了我很多时间。谢谢。
chrislarson

3
如果没有返回值,有没有办法做到这一点?即func updateProperty<T>( propertyID : String )
Kyle Bashour '16

1
我不明白您为什么要这么做?由于您不返回任何内容,因此声明函数泛型没有任何好处。
ThottChief

@ThottChief有很多好处,例如,如果您正在使用/构建键路径,或者将类型名称作为字符串获取,等等
。– zaitsman

很好的回答,谢谢。我希望let value = foo() as? Type可以使用它,因此可以在中使用它,if或者guard结果是可选的,但它不能...
agirault

53

迅捷5

通常,有许多方法可以定义通用函数。但它们基于T必须用作parameter或的条件return type

extension UIViewController {
    class func doSomething<T: UIView>() -> T {
        return T()
    }

    class func doSomethingElse<T: UIView>(value: T) {
        // Note: value is a instance of T
    }

    class func doLastThing<T: UIView>(value: T.Type) {
        // Note: value is a MetaType of T
    }
}

之后,我们必须T在致电时提供。

let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView
let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel
UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton
UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView

参考:

  1. http://austinzheng.com/2015/01/02/swift-generics-pt-1/
  2. https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2

解决一般问题的好方法...因为有很多解决方法。self.result = UIViewController.doSomething()只要您在声明属性时就输入属性,我也可以自己实现作品。
teradyl

解释很多事情的好答案。
Okhan Okbay

Java doLastThing<UITextView>()成为Swift doLastThing(UITextView.self),至少……这不是最糟糕的情况。比必须显式键入复杂的结果更好。感谢您的解决方法。
Erhannis

18

解决方案是以类类型为参数(如Java)

让编译器知道他正在处理什么类型,将类作为参数传递

extension UIViewController {
    func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){
        let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType
        before?(controller)
        self.navigationController?.pushViewController(controller, animated: true)
    }
}

致电为:

self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
        controller in
        controller.user = self.notification.sender
    })

1
这很棒,比接受的答案好得多。请考虑编辑以删除历史部分,或至少将其放在解决方案下方。谢谢!
Dan Rosenstark '17

1
@DanRosenstark感谢您的反馈:)
奥格汗Alikhanov

尽管这有效,但我认为这不是最佳实践。原因是现在函数定义正在决定是否存在强制转换问题时该怎么做。因此,如果强制转换失败,您将在此处崩溃。最好让客户决定要做什么,因为这可能会因客户而异。因此,出于这个原因,我将对此答案投反对票。
smileBot

@smileBot您是说这个例子不好还是方法?
Orkhan Alikhanov

该方法。我认为这是在实用时推迟专业化的一般编程规则。这是理解责任的想法的一部分。泛型不是功能的职责。这是调用者的责任,因为该函数无法知道所有调用者都需要什么。如果该功能担当此角色,则这将限制该功能的使用而不会带来任何收益。您可以将其概括为许多编码问题。这个单一的概念极大地帮助了我。
smileBot

4

由于您具有静态类型(作为参数的字符串),因此此处不需要泛型,但是如果要调用泛型函数,则可以执行以下操作。

使用通用方法

func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T {
    if let existing = fetchExisting(type) {
       return existing
    }
    else {
        return createNew(type)
    }
}

func fetchExisting<T: NSManagedObject>(type: T.Type) -> T {
    let entityName = NSStringFromClass(type)
     // Run query for entiry
} 

func createNew<T: NSManagedObject>(type: T.Type) -> T {
     let entityName = NSStringFromClass(type)
     // create entity with name
} 

使用泛型类(由于每个实例只能为一种类型定义泛型,因此灵活性较差)

class Foo<T> {

   func doStuff(text: String) -> T {
      return doOtherStuff(text)
   }

   func doOtherStuff(text: String) -> T {

   }  

}

let foo = Foo<Int>()
foo.doStuff("text")

3

我认为,当您指定泛型函数时,应指定一些T类型的参数,如下所示:

func generic1<T>(parameter: T) {
    println("OK")
}

func generic2<T>(parameter: T) {
    generic1(parameter)
}

如果要调用handle()方法,则可以通过编写协议并为T指定类型约束来实现:

protocol Example {
    func handle() -> String
}

extension String: Example {
    func handle() -> String {
        return "OK"
    }
}

func generic1<T: Example>(parameter: T) {
    println(parameter.handle())
}

func generic2<T: Example>(parameter: T) {
    generic1(parameter)
}

因此您可以使用String调用此泛型函数:

generic2("Some")

它会编译


1

到目前为止,我个人的最佳做法是使用@ orkhan-alikhanov的答案。如今,在查看SwiftUI以及实现方式.modifier()ViewModifier实现方式时,我发现了另一种方式(或更有效的解决方法?)

只需将第二个函数包装到中struct

例:

如果这给您“无法显式专门化泛型函数”

func generic2<T>(name: String){
     generic1<T>(name)
}

这可能会有所帮助。将的声明包装generic1struct

struct Generic1Struct<T> {
    func generic1(name: String) {## do, whatever it needs with T ##}
}

并调用:

func generic2<T>(name : String){
     Generic1Struct<T>().generic1(name: name)
}

备注:

  • 发生此错误消息时,我不知道是否有任何帮助。我只知道,这件事发生时我被困了很多次。我确实知道,当出现错误消息时,该解决方案在今天有所帮助。
  • Swift处理泛型的方式对我来说仍然令人困惑。
  • 此示例以及使用的变通方法struct是一个很好的示例。这里的解决方法没有更多信息-但可以通过编译器。信息相同,但结果不同?然后出了点问题。如果是编译器错误,则可以修复。

0

我的通用类函数也有类似的问题class func retrieveByKey<T: GrandLite>(key: String) -> T?

我不能称其为let a = retrieveByKey<Categories>(key: "abc")Categories是GrandLite的子类的地方。

let a = Categories.retrieveByKey(key:"abc")返回GrandLite,而不是类别。泛型函数不会根据调用它们的类来推断类型。

class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?当我尝试let a = Categories.retrieveByKey(aType: Categories, key: "abc")给我一个错误时,它给了我一个错误,即即使Categories是GrandLite的子类,也无法将Categories.Type转换为GrandLite。然而...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? 工作,如果我试图let a = Categories.retrieveByKey(aType: [Categories](), key: "abc")显然是一个子不工作的明确分配,但使用其他泛型类型(阵列)的隐式分配新建分配FY确实在斯威夫特3的工作。


1
要解决该错误,您必须提供aType作为的实例T,而不是将Categories其自身放置。例如:let a = Categories.retrieveByKey(aType: Categories(), key: "abc")。另一个解决方案是定义aType: T.Type。然后将该方法称为let a = Categories.retrieveByKey(aType: Categories.self, key: "abc")
nahung89 '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.