Swift中的泛型 - “通用参数'T'无法推断

时间:2016-08-17 14:11:44

标签: ios generics swift3

我想从方法中返回符合UIViewController的{​​{1}},所以我正在使用方法签名:

MyProtocol

首先我不明白:如果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? } TMyViewController的子类并且符合协议,因此不需要强制转换,对吧?

3 个答案:

答案 0 :(得分:27)

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()

在这种情况下,TSomeOtherViewControllerMyViewController不是那种类型,因此您使用as!演员表所做的事情很危险。

答案 1 :(得分:10)

在这样的方法中,返回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的唯一地方是通过返回值。 <>之间的所有代码都是T允许的约束。 -> T是唯一可以在约束之外看到T的地方。因此,如果我们能以某种方式告诉编译器我们希望myMethod返回什么,我们已经给出了足够的信息来推断T

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

let vc: MyViewController = myMethod()

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

答案 2 :(得分:1)

正如评论中的某些人指出的那样,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