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

swa*_*ner 44 generics ios swift3

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

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
Run Code Online (Sandbox Code Playgroud)

首先我不明白:如果myMethod返回例如MyViewController必须遵循签名,我必须强制施放它:

class MyViewController: UIViewController, MyProtocol
Run Code Online (Sandbox Code Playgroud)

我不能简单return MyViewController()但我需要这样投:return MyViewController() as! T- 为什么这是必要的?

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

let x = myMethod() as? UIViewController
Run Code Online (Sandbox Code Playgroud)

因为我得到了错误

Generic parameter 'T' could not be inferred
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现这样的目标?如果我把它投射到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?
}
Run Code Online (Sandbox Code Playgroud)

好的,我确实得到了一个部分,但为什么演员T必要?MyViewControllerUIViewController协议的子类,并且符合协议,所以不需要演员,对吧?

Rob*_*ier 32

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T
Run Code Online (Sandbox Code Playgroud)

该声明说:存在一个所谓的功能myMethod是,这种myMethod返回一些特定的 T地方T是一个亚型UIViewController,也MyProtocol.这并没有说T实际上是什么类型,并没有说只有一个这样的类型myMethod.如果有许多类型都是子类UIViewController并且符合,则可以有许多类型MyProtocol.这些类型中的每一个都创建了一个新版本myMethod(实际上是断言的新解决方案myMethod,这样的函数确实存在).

这与以下内容不同:

func myMethod() -> UIViewController
Run Code Online (Sandbox Code Playgroud)

那说:该函数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?
}
Run Code Online (Sandbox Code Playgroud)

T是调用者决定的特定类型.它不是"任何符合的类型",而是"符合某种特定的具体类型".考虑你打电话的情况:

let vc: SomeOtherViewController = myMethod()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,TSomeOtherViewController.MyViewController不是那种类型,所以你对as!演员表现的是危险的.


don*_*als 10

在这样的方法中,返回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
}
Run Code Online (Sandbox Code Playgroud)

所以,考虑你的用法示例:

let x = myMethod()
Run Code Online (Sandbox Code Playgroud)

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

编译器可以推断出我们想要的唯一地方T是返回值.它们之间的所有代码<>都是T允许的约束.-> T是唯一T在限制之外看到的地方.因此,如果我们能够以某种方式告诉编译器我们想要myMethod返回什么,我们已经给出了足够的信息来推断T.

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

let vc: MyViewController = myMethod()
Run Code Online (Sandbox Code Playgroud)

通过指定类型vc,编译器了解我们想要myMethod返回一个MyViewController.所以现在T的类型可以推断,如果我们返回T,我们实际上会返回MyViewController.