Swift 中的泛型 - “无法推断出泛型参数‘T’

Posted

技术标签:

【中文标题】Swift 中的泛型 - “无法推断出泛型参数‘T’【英文标题】:Generics in Swift - "Generic parameter 'T' could not be inferred 【发布时间】: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 并符合特定协议的类型;我确实有符合此规则的不同类,因此我不想使用特定类型。 您不想接受答案吗? 【参考方案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

【讨论】:

以上是关于Swift 中的泛型 - “无法推断出泛型参数‘T’的主要内容,如果未能解决你的问题,请参考以下文章

符合 Swift 协议的泛型类型

swift4:使用func泛型类型来指定另一个func的泛型类型

Swift - 在具有可选参数的泛型函数中以 Nil 作为参数

OC的泛型使用介绍

Apple Swift:String 类型的泛型类型约束仅不起作用

请问下,C#的泛型数组中的 ToDictionary 方法怎么用? 详细说明请进。。