【问题标题】:Generics in Swift - "Generic parameter 'T' could not be inferredSwift 中的泛型 - “无法推断出泛型参数‘T’
【发布时间】:2016-08-17 14:11:43
【问题描述】:

我想从一个方法返回一个符合MyProtocolUIViewController,所以我使用方法签名:

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {

我不明白的第一件事:如果myMethod 返回例如MyViewController 必须遵循签名,我必须强制转换它:

class MyViewController: UIViewController, MyProtocol

我不能简单地return MyViewController(),但我需要这样转换:return MyViewController() as! T - 为什么有必要这样做?

第二件事:我怎样才能在某处使用这种方法?我不能简单地说

let x = myMethod() as? UIViewController

当我得到错误时

Generic parameter 'T' could not be inferred

我怎样才能实现这样的目标?如果我将它转换为 MyViewController 它可以工作,但我当然想避免这种情况。

编辑:示例

class MyViewController : UIViewController, MyProtocol {
}

protocol MyProtocol {
}

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

好的,我确实得到了一个角色,但为什么需要演员表TMyViewControllerUIViewController 的子类并且符合协议,所以不需要强制转换,对吧?

【问题讨论】:

  • 你到底想在这里实现什么?你为什么首先使用泛型?
  • 我想使用 UIViewController 并符合特定协议的类型;我确实有符合此规则的不同类,因此我不想使用特定类型。
  • 您不想接受答案吗?

标签: ios generics swift3


【解决方案1】:
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T

这个声明说:存在一个名为myMethod 的函数,这样myMethod 返回一些特定 T 其中TUIViewController 的子类型,也是MyProtocol .这并没有说T 实际上是什么类型,也没有说只有一个myMethod。如果有很多类型既是UIViewController 的子类又符合MyProtocol,则可能有很多类型。这些类型中的每一种都会创建一个新版本的myMethod(实际上是对myMethod 断言的新解决方案,即确实存在这样的函数)。

这与以下内容不同:

func myMethod() -> UIViewController

也就是说:函数myMethod 返回UIViewController 的任何子类型。

在 Swift 中没有办法表达“任何类型是 UIViewController 的子类并且是 MyProtocol 的子类型”。您只能讨论满足该标准的特定类型。 Swift 不能以这种方式组合类和协议;这只是语言的当前限制,而不是深层设计问题。

具体任何是个问题。有许多函数可以满足您的 myMethod 声明。您可以插入符合规则的每个T 都将成为候选人。所以当你说myMethod()时,编译器不知道你指的是哪个具体的T

(我打算扩展这个答案,以更少的类型理论来提供它,更多地用“你如何用代码来做”术语来提供它,但 donnywals 已经有了一个很好的版本。)

* 对您编辑的问题 *

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
    return MyViewController() as! T // why is the cast necessary?
}

T 是由调用者决定的特定类型。它不是“任何符合的类型”,而是“某种特定的、具体的符合的类型”。考虑一下你打电话的情况:

let vc: SomeOtherViewController = myMethod()

在这种情况下,TSomeOtherViewControllerMyViewController 不是那种类型,所以你用 as! 演员做的事情很危险。

【讨论】:

    【解决方案2】:

    在这样的方法中,返回T 意味着你必须返回T。如果返回MyViewController,则返回类型应为MyViewControllerT 是一个泛型类型,它将采用 Swift 编译器可以推断出的任何形式。

    因此,使用您的方法签名,协议和方法的简单实现可能如下所示。

    protocol MyProtocol {
        var name: String { get set }
    }
    
    func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
        var vc = T()
        vc.name = "Hello, world"
        return vc
    }
    

    因此,考虑您的使用示例:

    let x = myMethod()
    

    编译器如何知道T 的具体类型是什么?没有任何暗示MyViewController。我们唯一知道的是,无论T 是什么,它都应该是MyViewController 或其子类。它应该符合MyProtocol。但这并没有提供关于 T 应该是什么类型的信息。

    编译器可以推断出我们想要T 的唯一地方是通过返回值。 &lt;&gt; 之间的所有代码都是允许 T 的约束。 -&gt; T 是在约束之外看到T 的唯一地方。因此,如果我们能以某种方式告诉编译器我们希望 myMethod 返回什么,我们就已经为它提供了足够的信息来推断 T

    您的类型转换有效,但我同意它不是很漂亮。编译器推断T 的一个更漂亮的方法是这样的。

    let vc: MyViewController = myMethod()
    

    通过指定vc 的类型,编译器知道我们希望myMethod 返回MyViewController。所以现在可以推断出T 的类型,如果我们返回T,我们实际上会返回MyViewController

    【讨论】:

    • @donnywals 泛型方法参数怎么样,而不是返回类型?你如何推断​​出这样的事情?
    【解决方案3】:

    正如 cmets 中的一些人所指出的,myMethod 没有明显的理由是通用的。这样做的理由是:(引用您的评论)

    我想使用 UIViewController 并符合特定协议的类型;

    让我们称该类型为ViewControllerAndMyprotocol

    我确实有符合此规则的不同类,因此我不想使用特定类型

    但是myMethod 签名已经限制了ViewControllerAndMyprotocol 类型,即调用者一定会收到UIViewController 而不是任何“符合此规则的不同类”。

    具体类型可以是ViewControllerAndMyprotocol,包括MyViewController,这就是为什么在需要强制转换的语句let x = myMethod() 中存在类型歧义的原因:let x = myMethod() as? UIViewController

    您可以通过更改myMethod 签名来避免强制转换:

    typealias ViewControllerAndMyprotocol = UIViewController & MyProtocol
    
    func myMethod() -> ViewControllerAndMyprotocol {
       return MyViewController()
    }
    

    语句let x = myMethod() 不需要强制转换,其类型为ViewControllerAndMyprotocol,它也是UIViewController

    【讨论】:

      猜你喜欢
      • 2021-11-06
      • 1970-01-01
      • 2015-08-03
      • 2020-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-11
      • 1970-01-01
      相关资源
      最近更新 更多