Comment renvoyer de la valeur avec une méthode anonyme?

89

Cela échoue

string temp = () => {return "test";};

avec l'erreur

Impossible de convertir l'expression lambda en type «chaîne» car il ne s'agit pas d'un type délégué

Que signifie l'erreur et comment puis-je la résoudre?

4e espace
la source
Pourquoi cette question est-elle le premier résultat dans Google lors de la recherche de l'erreur "la fonction anonyme convertie en un délégué retournant vide ne peut pas renvoyer une valeur" alors que cela n'a clairement rien à voir avec elle?
Calmarius

Réponses:

136

Le problème ici est que vous avez défini une méthode anonyme qui renvoie a stringmais que vous essayez de l'attribuer directement à un string. C'est une expression qui, lorsqu'elle est invoquée, produit un stringce n'est pas directement un string. Il doit être affecté à un type de délégué compatible. Dans ce cas, le choix le plus simple estFunc<string>

Func<string> temp = () => {return "test";};

Cela peut être fait en une ligne par un peu de conversion ou en utilisant le constructeur de délégué pour établir le type du lambda suivi d'un appel.

string temp = ((Func<string>)(() => { return "test"; }))();
string temp = new Func<string>(() => { return "test"; })();

Remarque: les deux échantillons peuvent être raccourcis à la forme d'expression qui n'a pas { return ... }

Func<string> temp = () => "test";
string temp = ((Func<string>)(() => "test"))();
string temp = new Func<string>(() => "test")();
JaredPar
la source
Merci. Donc pas moyen de tout faire sur une seule ligne (y compris l'attribution d'une chaîne)? La valeur que je veux ("test", qui est en fait une variable dans la vraie vie) est à l'intérieur d'un autre lambda donc je perds la portée si j'essaye de définir comme vous l'avez fait ci-dessus.
4thSpace
@ 4thSpace, cela peut être fait en une seule ligne avec un casting maléfique. J'ai mis à jour ma réponse pour montrer le chemin
JaredPar
Ou dans ce cas, juste Func<string> temp = () => "test";.
Gabe
Ou dans le cas de votre montage,string temp = new Func<string>(() => "test")();
Gabe
Parfait! Si je voulais passer un int, pouvez-vous le montrer sur une seule ligne? J'ai essayé ceci mais pas aller: ((Func <int, string>) ((4) => {return "test";})) ();
4thSpace
15

Vous essayez d'affecter un délégué de fonction à un type chaîne. Essaye ça:

Func<string> temp = () => {return "test";};

Vous pouvez maintenant exécuter la fonction ainsi:

string s = temp();

La variable "s" aura maintenant la valeur "test".

Dave Swersky
la source
1
Cela ne compile pas: "Impossible d'attribuer une expression lambda à une variable locale implicitement typée"
Dave Bish
@Dave: Intéressant, je ne connaissais pas cette limitation. Mis à jour, merci!
Dave Swersky
8

En utilisant une petite fonction d'aide et des génériques, vous pouvez laisser le compilateur déduire le type et le raccourcir un peu:

public static TOut FuncInvoke<TOut>(Func<TOut> func)
{
    return func();
}

var temp = FuncInvoke(()=>"test");

Note latérale: c'est également bien car vous pouvez ensuite renvoyer un type anonyme:

var temp = FuncInvoke(()=>new {foo=1,bar=2});
joeriks
la source
Technique intéressante. Cela ajoute-t-il une surcharge au moment de l'exécution, ou est-ce tout au moment de la compilation?
ToolmakerSteve
@ToolmakerSteve: Je suppose que cela ajouterait un peu de surcharge d'exécution (il encapsule un appel à une méthode anonyme dans une autre méthode) - cependant, je soupçonne que cela dépendrait également de l'endroit où la méthode FuncInvoke a été définie (même assemblage que où il est appelé vs assembly différent, etc.), car cela pourrait être le genre de chose que le compilateur pourrait "en ligne". C'est le genre de question à laquelle les gens répondent en écrivant un programme de test rapide, en compilant puis en sélectionnant l'IL qui en résulte.
Daniel Scott
@ToolmakerSteve Suite à cette dernière «estimation» de l'impact sur les performances, j'ajouterais que même le pire des cas que cela aurait sur les performances serait pratiquement nul (un appel de fonction supplémentaire, à une méthode statique non virtuelle). Quiconque utilise cette technique le fait probablement parce qu'il lance des lambdas. Cela signifie qu'ils utilisent probablement au moins quelques méthodes d'extension LINQ quelque part, de sorte que les chances sont bonnes à bonnes qu'ils aient enchaîné par inadvertance deux méthodes LINQ ensemble d'une manière qui nuit aux performances 100000 fois pire qu'un appel de fonction supplémentaire ;)
Daniel Scott
5

vous pouvez utiliser une méthode anonyme avec argument:

int arg = 5;

string temp = ((Func<int, string>)((a) => { return a == 5 ? "correct" : "not correct"; }))(arg);
HamidReza
la source
Vous pouvez, mais veuillez expliquer en quoi il s'agit d'une réponse à la question.
ToolmakerSteve
2

Une méthode anonyme peut renvoyer une valeur à l'aide d'un délégué func. Voici un exemple où j'ai montré comment renvoyer une valeur à l'aide d'une méthode anonyme.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {


        static void Main(string[] args)
        {
            Func<int, int> del = delegate (int x)
              {
                  return x * x;

              };

            int p= del(4);
            Console.WriteLine(p);
            Console.ReadLine();
        }
    }
}
Debendra Dash
la source
0

Ceci est un autre exemple utilisant C # 8 ( pourrait également fonctionner avec d'autres versions .NET prenant en charge des tâches parallèles )

using System;
using System.Threading.Tasks;

namespace Exercise_1_Creating_and_Sharing_Tasks
{
    internal static class Program
    {
        private static int TextLength(object o)
        {
            Console.WriteLine($"Task with id {Task.CurrentId} processing object {o}");
            return o.ToString().Length;
        }

        private static void Main()
        {
            const string text1 = "Welcome";
            const string text2 = "Hello";

            var task1 = new Task<int>(() => TextLength(text1));
            task1.Start();

            var task2 = Task.Factory.StartNew(TextLength, text2);

            Console.WriteLine($"Length of '{text1}' is {task1.Result}");
            Console.WriteLine($"Length of '{text2}' is {task2.Result}");

            Console.WriteLine("Main program done");
            Console.ReadKey();
        }
    }
}
Wbadry
la source