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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38999102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:56:32  来源:igfitidea点击:

Generics in Swift - "Generic parameter 'T' could not be inferred

iosgenericsswift3

提问by swalkner

I'd like to return a UIViewControllerconforming to MyProtocolfrom a method, so I'm using the method signature:

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

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

First thing I don't understand: if myMethodreturns e.g. a MyViewControllerwhich has to following signature, I have to force cast it:

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

class MyViewController: UIViewController, MyProtocol

I cannot simply return MyViewController()but I need to cast it like this: return MyViewController() as! T- why is this necessary?

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

And the second thing: how can I use this method somewhere? I cannot simply say

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

let x = myMethod() as? UIViewController

as I get the error

当我收到错误时

Generic parameter 'T' could not be inferred

How can I achieve something like this? If I cast it to MyViewControllerit works, but I would like to avoid that of course.

我怎样才能实现这样的目标?如果我将其转换MyViewController为有效,但我当然想避免这种情况。

EDIT: Example

编辑:示例

class MyViewController : UIViewController, MyProtocol {
}

protocol MyProtocol {
}

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

ok, I do get one part, but why is the cast to Tnecessary? MyViewControlleris a subclass of UIViewControllerand conforms to the protocol, so no cast should be necessary, right?

好的,我确实得到了一部分,但为什么T需要演员阵容?MyViewControllerUIViewController符合协议的子类,所以不需要强制转换,对吧?

回答by Rob Napier

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

This declaration says: There exists a function called myMethod, such that myMethodreturns some specificTwhere Tis a subtype of UIViewControllerand also MyProtocol. This does not say what type Tactually is, and it does not say that there is only one such myMethod. There can be many if there are many type that are both subclasses of UIViewControllerand conform to MyProtocol. Every one of those types creates a new version of myMethod(really a new solution to the assertion myMethodmakes, that such a function does exist).

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

This is not the same thing as:

这与以下内容不同:

func myMethod() -> UIViewController

That says: The function myMethodreturns any subtype of UIViewController.

也就是说:该函数myMethod返回UIViewController.

There is no way in Swift to express "any type that is a subclass of UIViewController and is a subtype of MyProtocol." You can only discuss a specific type that meets that criterial. Swift can't combine classes and protocols this way; it's just a current limitation of the language, not a deep design issue.

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

The specificversus anyis the issue. There are many functions that satisfy your myMethoddeclaration. Every Tyou can plug in that conforms to the rules would be a candidate. So when you say myMethod(), the compiler doesn't know which specific Tyou mean.

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

(I was going to expand this answer to provide it in less type-theory, more "how do you do it in code" terms, but donnywals already has an excellent version of that.)

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

* To your edited question *

* 对于您编辑的问题 *

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

Tis a specifictype decided by the caller. It is not "any type that conforms" it is "some specific, concrete type that conforms." Consider the case that you called:

T是由调用者决定的特定类型。它不是“任何符合的类型”,而是“符合的某种特定的、具体的类型”。考虑您调用的案例:

let vc: SomeOtherViewController = myMethod()

In this case, Tis SomeOtherViewController. MyViewControlleris not that type, so what you're doing with the as!cast is dangerous.

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

回答by donnywals

In a method like this, returning Tmeans you have to return T. If you return MyViewController, the return type should be MyViewController. Tis a generic type that will take the form of whatever the Swift compiler can infer it to be.

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

So, with your method signature, a simple implementation of the protocol and method could look like this.

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

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
}

So, considering your usage example:

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

let x = myMethod()

How would the compiler know what the concrete type of Tis? There is nothing giving it a hint of MyViewController. The only thing we know is that whatever Tis, it should be MyViewControlleror a subclass of it. And it should conform to MyProtocol. But this does not provide information about what the type of Tshould be.

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

The only place where the compiler can infer what we want Tto be is through the return value. All the code between <>are constraints for what Tis allowed to be. -> Tis the only place where Tis seen outside of the constraints. So if we can somehow tell the compiler what we want myMethodto return, we have given it enough information to infer T.

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

Your typecast works but I agree that it's not very pretty. A much prettier way for the compiler to infer Tis this.

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

let vc: MyViewController = myMethod()

By specifying the type of vc, the compiler understands that we want myMethodto return a MyViewController. So now T's type can be inferred and if we return T, we actually return MyViewController.

通过指定 的类型vc,编译器知道我们想要myMethod返回一个MyViewController。所以现在T的类型可以被推断出来,如果我们返回T,我们实际上返回MyViewController

回答by Lukas

As some pointed out in the comments, there is no apparent reason for myMethodto be generic. The argument for doing so is: (quoting from your comment)

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

I would like to work with a type that's a UIViewController and conforms to the specific protocol;

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

Lets call that type ViewControllerAndMyprotocol,

让我们称之为类型ViewControllerAndMyprotocol

I do have different classes which conform to this rules, therefore I do not want to use a specific type

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

However myMethodsignature already constrains the type ViewControllerAndMyprotocoli.e. caller is bound to receive a UIViewControllerand not any of the "different classes which conform to this rules".

然而,myMethod签名已经限制了类型,ViewControllerAndMyprotocol即调用者必须接收 aUIViewController而不是任何“符合此规则的不同类”。

The flexibility on what concrete types could be ViewControllerAndMyprotocol, including MyViewControlleris why there is type ambiguity in the statement let x = myMethod()requiring casting: let x = myMethod() as? UIViewController

具体类型的灵活性ViewControllerAndMyprotocol,包括MyViewController为什么在let x = myMethod()需要强制转换的语句中存在类型歧义:let x = myMethod() as? UIViewController

You can avoid the casting by changing myMethodsignature as such:

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

typealias ViewControllerAndMyprotocol = UIViewController & MyProtocol

func myMethod() -> ViewControllerAndMyprotocol {
   return MyViewController()
}

The statement let x = myMethod()will not require casting and will be of type ViewControllerAndMyprotocolwhich is also a UIViewController.

该语句let x = myMethod()不需要强制转换,并且其类型ViewControllerAndMyprotocol也是UIViewController.