web-dev-qa-db-fra.com

Impossible d'utiliser le paramètre ref ou out dans les expressions lambda

Pourquoi ne pouvez-vous pas utiliser un paramètre ref ou out dans une expression lambda?

Je suis tombé sur l'erreur aujourd'hui et j'ai trouvé une solution de contournement, mais j'étais toujours curieux de savoir pourquoi il s'agit d'une erreur de compilation.

CS1628 : Impossible d'utiliser le paramètre 'paramètre' dans une méthode anonyme, une expression lambda ou une expression de requête

Voici un exemple simple:

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    int newValue = array.Where(a => a == value).First();
}
155
skalb

Les lambda ont l’air de changer la durée de vie des variables qu’ils capturent. Par exemple, l'expression lambda suivante provoque le paramètre p1 à live plus long que le cadre de la méthode actuelle, car sa valeur est accessible une fois que le cadre de la méthode n'est plus dans la pile.

Func<int> Example(int p1) {
  return () => p1;
}

Une autre propriété des variables capturées est que les modifications apportées à la variable sont également visibles en dehors de l'expression lambda. Par exemple, les impressions suivantes 42

void Example2(int p1) {
  Action del = () => { p1 = 42; }
  del();
  Console.WriteLine(p1);
}

Ces deux propriétés produisent un certain ensemble d’effets qui volent face à un paramètre ref de la manière suivante:

  • les paramètres de référence peuvent avoir une durée de vie fixe. Envisagez de passer une variable locale en tant que paramètre ref à une fonction.
  • Les effets secondaires dans le lambda devraient être visibles sur le paramètre ref lui-même. À la fois dans la méthode et dans l'appelant.

Ce sont des propriétés quelque peu incompatibles et sont l’une des raisons pour lesquelles elles sont interdites dans les expressions lambda.

116
JaredPar

Sous le capot, la méthode anonyme est implémentée en hissant les variables capturées (c'est en quoi consiste le corps de votre question) et en les stockant en tant que champs d'un compilateur. classe générée. Il n'y a aucun moyen de stocker un paramètre ref ou out en tant que champ. Eric Lippert en a parlé dans ne entrée de blog . Notez qu'il existe une différence entre les variables capturées et les paramètres lambda. Vous pouvez disposer de "paramètres formels" similaires aux suivants, car ils ne sont pas des variables capturées:

delegate void TestDelegate (out int x);
static void Main(string[] args)
{
    TestDelegate testDel = (out int x) => { x = 10; };
    int p;
    testDel(out p);
    Console.WriteLine(p);
}
81
Mehrdad Afshari

Vous pouvez mais vous devez définir explicitement tous les types

(a, b, c, ref d) => {...}

Est invalide, cependant

(int a, int b, int c, ref int d) => {...}

Est valable

61
Ben Adams

Comme c’est l’un des meilleurs résultats pour "C # lambda ref" sur Google; Je pense avoir besoin de développer les réponses ci-dessus. L'ancienne syntaxe de délégué anonyme (C # 2.0) fonctionne et prend en charge des signatures plus complexes (ainsi que des fermetures). Les délégués Lambda et anonymes ont au moins partagé la mise en œuvre perçue dans le back-end du compilateur (si elles ne sont pas identiques) - et plus important encore, elles prennent en charge les fermetures.

Ce que j'essayais de faire quand j'ai fait la recherche, pour montrer la syntaxe:

public static ScanOperation<TToken> CreateScanOperation(
    PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
    var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
    return delegate(string text, ref int position, ref PositionInformation currentPosition)
        {
            var token = oldScanOperation(text, ref position, ref currentPosition);
            if (token == null)
                return null;
            if (tokenDefinition.LeftDenotation != null)
                token._led = tokenDefinition.LeftDenotation(token);
            if (tokenDefinition.NullDenotation != null)
                token._nud = tokenDefinition.NullDenotation(token);
            token.Identifier = tokenDefinition.Identifier;
            token.LeftBindingPower = tokenDefinition.LeftBindingPower;
            token.OnInitialize();
            return token;
        };
}

Gardez simplement à l’esprit que les Lambdas sont plus sûrs du point de vue procédural et mathématique (en raison de la promotion de la valeur de référence mentionnée plus haut): vous pouvez ouvrir une boîte de Pandore. Réfléchissez bien lorsque vous utilisez cette syntaxe.

5
Jonathan Dickinson