Swift 中的泛型 - “无法推断出泛型参数‘T’
Posted
技术标签:
【中文标题】Swift 中的泛型 - “无法推断出泛型参数‘T’【英文标题】:Generics in Swift - "Generic parameter 'T' could not be inferred 【发布时间】:2016-08-17 14:11:43 【问题描述】:我想从一个方法返回一个符合MyProtocol
的UIViewController
,所以我使用方法签名:
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?
好的,我确实得到了一个角色,但为什么需要演员表T
? MyViewController
是 UIViewController
的子类并且符合协议,所以不需要强制转换,对吧?
【问题讨论】:
你到底想在这里实现什么?你为什么首先使用泛型? 我想使用UIViewController
并符合特定协议的类型;我确实有符合此规则的不同类,因此我不想使用特定类型。
您不想接受答案吗?
【参考方案1】:
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T
这个声明说:存在一个名为myMethod
的函数,这样myMethod
返回一些特定 T
其中T
是UIViewController
的子类型,也是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()
在这种情况下,T
是 SomeOtherViewController
。 MyViewController
不是那种类型,所以你用 as!
演员做的事情很危险。
【讨论】:
【参考方案2】:在这样的方法中,返回T
意味着你必须返回T
。如果返回MyViewController
,则返回类型应为MyViewController
。 T
是一个泛型类型,它将采用 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
的唯一地方是通过返回值。 <>
之间的所有代码都是允许 T
的约束。 -> 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’的主要内容,如果未能解决你的问题,请参考以下文章
swift4:使用func泛型类型来指定另一个func的泛型类型
Swift - 在具有可选参数的泛型函数中以 Nil 作为参数