145 votes

Pourquoi ne ' t une méthode anonyme être assignés à var ?

J'ai le code suivant:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

Cependant, le suivant ne compile pas:

var comparer = delegate(string value) {
    return value != "0";
};

Pourquoi ne peut pas le compilateur de figure, c'est un Func<string, bool>? Il prend une chaîne de caractère en paramètre, et retourne un booléen. Au lieu de cela, il me donne l'erreur:

Ne peut pas attribuer à une méthode anonyme implicitement de type de variable locale.

J'ai une réponse et qui est si la var version compilée, il serait le manque de cohérence si j'avais le suivant:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

Le ci-dessus ne ferait pas de sens, Func<> permet seulement jusqu'à 4 arguments (dans .NET 3.5, qui est ce que j'utilise). Peut-être quelqu'un pourrait clarifier le problème. Merci.

157voto

Eric Lippert Points 300275

D'autres l'ont déjà souligné qu'il existe une infinité de possibles délégué des types que vous pourriez dire; ce qui est si spécial à propos de Func qu'il mérite d'être la valeur par défaut à la place de Predicate ou Action ou toute autre possibilité? Et, pour les lambdas, pourquoi est-il évident que l'intention est de choisir le délégué forme, plutôt que l'expression de la forme de l'arbre?

Mais on pourrait dire qu' Func est spécial, et que le type inféré d'un lambda, d'anonymes ou de la méthode est la touche Func de quelque chose. Nous serions tout de même avoir toutes sortes de problèmes. Quels types aimeriez-vous être déduit pour les cas suivants?

var x1 = (ref int y)=>123;

Il n'y a pas d' Func<T> type qui prend un ref quoi que ce soit.

var x2 = y=>123;

Nous ne connaissons pas le type du paramètre formel, même si nous savons que le retour. (Ou est-ce nous? C'est le retour de type int? longue? court? octet?)

var x3 = (int y)=>null;

Nous ne connaissons pas le type de retour, mais il ne peut pas être nulle. Le type de retour peut être n'importe quel type de référence ou de tout nullable type de valeur.

var x4 = (int y)=>{ throw new Exception(); }

Encore une fois, nous ne connaissons pas le type de retour, et cette fois, il peut être vide.

var x5 = (int y)=> q += y;

Est celui destiné à être un vide-retour de la déclaration lambda ou quelque chose qui renvoie la valeur qui a été affecté à q? Les deux sont légales; qui devrions-nous choisir?

Maintenant, vous pourriez dire, eh bien, il suffit de ne pas l'appui de ces fonctionnalités. En plus de soutenir "normal" cas où les types peuvent être réglés. Cela n'aide pas. Comment est-ce que faire de ma vie plus facile? Si la fonctionnalité fonctionne parfois et échoue parfois je dois encore écrire le code pour détecter toutes ces situations de défaillance et de donner un message d'erreur significatif pour chacun d'eux. Nous avons encore à spécifier toutes qu'un comportement, d'un document, d'écrire des tests pour elle, et ainsi de suite. C'est un très coûteux fonctionnalité qui permet d'économiser l'utilisateur peut-être une demi-douzaine de frappes. Nous avons de meilleures façons d'ajouter de la valeur à la langue que de passer beaucoup de temps à écrire des cas de test pour une fonctionnalité qui ne fonctionne pas la moitié du temps et ne fournit pas loin de bénéficier dans les cas où il ne fonctionne pas.

La situation où il est réellement utile:

var xAnon = (int y)=>new { Y = y };

parce qu'il n'y a pas de "speakable" type pour cette chose. Mais nous avons ce problème tout le temps, et il nous suffit d'utiliser la méthode d'inférence de types de déduire le type:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

et maintenant, la méthode d'inférence de type œuvres ce que la touche func type est.

31voto

itowlson Points 44174

Seul Eric Lippert sait pour sûr, mais je pense que c'est parce que la signature du délégué type n'est pas uniquement de déterminer le type.

Considérez votre exemple:

var comparer = delegate(string value) { return value != "0"; };

Voici deux inférences pour ce qui de la var doit être:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

Qui l'on doit le compilateur en déduire? Il n'y a pas de bonne raison de choisir l'un ou l'autre. Et bien qu'un Predicate<T> est fonctionnellement équivalent à un Func<T, bool>, ils sont encore différents types au niveau de l' .NET type de système. Le compilateur ne peut donc pas sans ambiguïté résoudre le type de délégué, et doit échouer l'inférence de type.

6voto

Brian Rasmussen Points 68853

Eric Lippert a un vieux post à ce sujet, où il dit

Et en fait, le C# 2.0 les appels. Méthode de groupe expressions et méthode anonyme les expressions sont des expressions sans type en C# 2.0, et les expressions lambda rejoindre dans C# 3.0. Par conséquent, il est illégal pour eux d'apparaître "nue" sur le côté droit de l'implicite de la déclaration.

5voto

Stephen Cleary Points 91731

Les différents délégués sont considérés comme différents types. par exemple, Action est différent de celui d' MethodInvoker, et une instance d' Action ne peut pas être affectée à une variable de type MethodInvoker.

Donc, étant donné un délégué anonyme (ou lambda) comme () => {}, est-il un Action ou MethodInvoker? Le compilateur ne peut pas dire.

De même, si je déclare un type délégué de prendre un string argument et retournant une bool, comment le compilateur de savoir que vous avez vraiment envie d'un Func<string, bool> à la place de mon type de délégué? Il ne peut pas en déduire le type de délégué.

2voto

nybbler Points 3267

Les points suivants sont à partir de MSDN concernant Implicitement Tapé Variables Locales:

  1. var ne peut être utilisé lorsqu'une variable locale est déclarée et initialisée dans la même déclaration; la variable ne peut pas être initialisé à null, ou à une méthode ou une fonction anonyme.
  2. Le mot-clé var demande au compilateur de déduire le type de la variable à partir de l'expression sur le côté droit de l'instruction d'initialisation.
  3. Il est important de comprendre que le mot-clé var ne signifie pas "variante" et de ne pas indiquer que la variable est faiblement typé, ou tardive. Cela signifie simplement que le compilateur détermine et attribue le type le plus approprié.

Référence MSDN: Implicitement Tapé les Variables Locales

Considérant les éléments suivants concernant les Méthodes Anonymes:

  1. Les méthodes anonymes de vous permettre de vous omettez le paramètre de la liste.

Référence MSDN: Méthodes Anonymes

Je serais tenté de croire que, puisque la méthode anonyme peuvent en réalité avoir différentes signatures de méthode, le compilateur n'est pas en mesure de bien déterminer ce à quoi le type le plus approprié pour affecter serait.

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