53 votes

Les génériques Swift - "paramètre Générique de 'T' ne peut pas être déduit

J'aimerais revenir un UIViewController conforme MyProtocol à partir d'une méthode, donc je suis en utilisant la signature de la méthode:

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

La première chose que je ne comprends pas: si myMethod retours par exemple, un MyViewController qui a de la suite de la signature, j'ai à force de voter pour elle:

class MyViewController: UIViewController, MyProtocol

Je ne peux pas simplement return MyViewController() mais j'ai besoin de le jeter comme ceci: return MyViewController() as! T - pourquoi est-ce nécessaire?

Et la deuxième chose: comment puis-je utiliser cette méthode quelque part? Je ne peut pas simplement dire

let x = myMethod() as? UIViewController

comme j'ai l'erreur

Generic parameter 'T' could not be inferred

Comment puis-je obtenir quelque chose comme cela? Si je jette - MyViewController il fonctionne, mais je voudrais éviter que des cours.

EDIT: Exemple

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, je fais une partie, mais pourquoi le cast T - elle nécessaire? MyViewController est une sous-classe de UIViewController et est conforme au protocole, donc pas de plâtre doivent être nécessaires, à droite?

39voto

Rob Napier Points 92148
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T

Cette déclaration dit: Il existe une fonction appelée myMethod, tel que myMethod certains retours spécifique TT est un sous-type d' UIViewController et également MyProtocol. Cela ne veut pas dire que ce type T n'est en réalité, et il ne dit pas qu'il n'y a qu'un seul myMethod. Il peut y avoir de nombreux cas, si il y a beaucoup de type qui sont à la fois des sous-classes d' UIViewController et conformes à l' MyProtocol. Chacun de ces types de crée une nouvelle version de l' myMethod (vraiment une nouvelle solution à l'assertion myMethod fait, qu'une telle fonction n'existe pas).

Ce n'est pas la même chose que:

func myMethod() -> UIViewController

Qui dit: La fonction myMethod renvoie n'importe quel sous-type de l' UIViewController.

Il n'existe aucun moyen rapide de s'exprimer n'importe quel type qui est une sous-classe de UIViewController et est un sous-type de MyProtocol." Vous ne pouvez discuter avec vous d'un type qui satisfait aux exigences criterial. Swift ne peut pas combiner les classes et les protocoles de cette façon; c'est juste une limitation du courant de la langue, pas un profond problème de conception.

Le spécifique de rapport à toute la question. Il y a beaucoup de fonctions qui répondent à vos myMethod déclaration. Chaque T vous pouvez brancher conforme aux règles serait un candidat. Donc quand vous dites myMethod(), le compilateur ne sait pas quel spécifiques T - vous dire.

(J'allais étendre cette réponse dans moins de type de théorie, plus de "comment voulez-vous faire dans le code", mais donnywals a déjà une excellente version.)

* Pour votre édité question *

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

T est un spécifique de type décidé par l'appelant. Il n'est pas "n'importe quel type qui est conforme", il est "certains, type de béton conforme." Prenons le cas que vous avez appelé:

let vc: SomeOtherViewController = myMethod()

Dans ce cas, T est SomeOtherViewController. MyViewController n'est pas ce type, de sorte que vous êtes en train de faire avec l' as! cast est dangereux.

15voto

donnywals Points 2733

Dans une méthode de ce genre, de revenir T signifie que vous avez à revenir T. Si vous retournez MyViewController, le type de retour doit être MyViewController. T est un type générique qui prendra la forme de ce que l'Swift compilateur peut déduire qu'elle soit.

Ainsi donc, avec votre signature de la méthode, une simple mise en œuvre du protocole et la méthode pourrait ressembler à ceci.

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
}

Aussi, compte tenu de votre exemple d'utilisation:

let x = myMethod()

Comment le compilateur de savoir ce que le béton type d' T est? Il n'y a rien en lui donnant un soupçon d' MyViewController. La seule chose que nous savons, c'est que, quelle que soit T , elle devrait être MyViewController ou une sous-classe. Et il devrait se conformer à l' MyProtocol. Mais cela ne veut pas fournir d'information sur le type d' T devraient l'être.

Le seul endroit où le compilateur peut déduire de ce que nous voulons T d'être, c'est par le biais de la valeur de retour. Tout le code entre <> sont des contraintes pour ce qu' T est autorisée. -> T est le seul endroit où l' T est vu en dehors des contraintes. Donc, si nous pouvons en quelque sorte de dire au compilateur que nous voulons myMethod de retour, nous ont donné suffisamment d'informations pour en déduire T.

Votre catalogué fonctionne, mais je suis d'accord que c'est pas très jolie. Une bien jolie façon pour le compilateur de déduire T est-ce.

let vc: MyViewController = myMethod()

En précisant le type d' vc, le compilateur comprend que nous voulons myMethod - retour d'un MyViewController. Alors maintenant, Ts'type peut être déduite et si nous revenons T, nous fait revenir MyViewController.

4voto

Lukas Points 2891

Comme certains l'ont souligné dans les commentaires, il n'y a aucune raison apparente pour myMethod être générique. L'argument pour le faire est la suivante: (en citant votre commentaire)

Je voudrais travailler avec un type qui est un UIViewController et est conforme au protocole spécifique;

Appelons ce type ViewControllerAndMyprotocol,

J'ai différentes classes qui sont conformes à ces règles, donc je ne veux pas utiliser un type spécifique

Toutefois myMethod signature déjà contraint le type ViewControllerAndMyprotocol c'est à dire de l'appelant est tenu de recevoir un UIViewController , et non sur les "classes différentes qui sont conformes à ce règlement".

La flexibilité sur quels types de béton pourrait être ViewControllerAndMyprotocol, y compris l' MyViewController est pourquoi il est de type ambiguïté dans l'énoncé let x = myMethod() nécessitant casting: let x = myMethod() as? UIViewController

Vous pouvez éviter le casting en changeant myMethod signature en tant que tel:

typealias ViewControllerAndMyprotocol = UIViewController & MyProtocol

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

L'énoncé let x = myMethod() ne nécessitera pas de casting et sera de type ViewControllerAndMyprotocol qui est aussi un UIViewController.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X