31 votes

Pourquoi un lambda sans capture est-il passé d'une statique en C # 5 à une méthode d'instance en C # 6?

Ce code génère une exception sur la ligne marquée:

using System;
using System.Linq.Expressions;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Action<int, int> a = (x, y) => Console.WriteLine(x + y);

            ParameterExpression p1 = Expression.Parameter(typeof(int), "p1");
            ParameterExpression p2 = Expression.Parameter(typeof(int), "p2");

            // Here is the exception ArgumentNullException.
            MethodCallExpression call = Expression.Call(a.Method, p1, p2);
        }
    }
}

Maintenant, j'ai testé ce code dans VS2013 (fonctionne comme un charme) et dans VS2015 de la Communauté (lève l'exception).

J'ai suivi les .Net de Source de Référence, ce qui m'a conduit à certains code condition qui checkes si l'fournis méthode IsStatic ou pas.

Dans mon cas, la méthode que j'ai pass (a.Method) est statique dans VS2013 et pour une raison non statique (exemple) dans VS2015. Si non, il se lève, me disant que je n'ai pas l'approvisionnement de l' Instance argument.

Pourquoi est-il si? Comment cela peut-il être évité afin qu' Expression.Call serait de commencer à travailler à nouveau dans Visual Studio?

22voto

Peter O. Points 9967

Roslyn (le compilateur C# utilisé par VS 2015) a tout changé lambda méthodes non statiques les méthodes, si ils le capturent ou non des variables. Voir Déléguer la mise en cache des changements de comportement dans Roslyn. Comme je l'explique, c'est un problème parce que les méthodes anonymes (comme ceux en cause ici) qui ne sont pas de capture de variables ont moins de vie que ceux qui le font. Cela ne veut pas dire, cependant, que ces méthodes doivent être statique: ce n'est qu'un détail d'implémentation.

10voto

Rob Points 2095

Je n'ai pas de réponse sur la raison pour laquelle il en est ainsi (reproduit localement aussi).

Cependant, la réponse à:

Pourquoi en est-il ainsi? Comment cela peut-il être évité afin que Expression.Call recommence à fonctionner dans le nouveau Visual Studio?

Vous pouvez le faire (fonctionne sur les deux compilateurs):

 Action<int, int> a = (x, y) => Console.WriteLine(x + y);

ParameterExpression p1 = Expression.Parameter(typeof(int), "p1");
ParameterExpression p2 = Expression.Parameter(typeof(int), "p2");

MethodCallExpression call;
if (a.Method.IsStatic)
{
    call = Expression.Call(a.Method, p1, p2);
}
else
{
    call = Expression.Call(Expression.Constant(a.Target), a.Method, p1, p2);
}
 

Merci à Jeppe Stig Nielsen pour le correctif concernant a.Target

4voto

Ivan Stoev Points 1156

Pourquoi est-il si?

Je ne sais pas pourquoi, et honnêtement était pas au courant de ce changement, mais en prenant un coup d'oeil sur le code décompilé a montré que pour tous semblables lambdas à l'intérieur de la classe Roslyn génère instance méthodes dans un singleton classe imbriquée appelés <>c comme ceci

internal class Program
{
    [CompilerGenerated]
    [Serializable]
    private sealed class <>c
    {
        public static readonly Program.<>c <>9;
        public static Action<int, int> <>9__0_0;

        static <>c()
        {
            Program.<>c.<>9 = new Program.<>c();
        }

        internal void <Main>b__0_0(int x, int y)
        {
            Console.WriteLine(x + y);
        }
    }
}

Pour moi, c'est une modification de rupture, mais je n'ai pas trouver toutes les informations à ce sujet.

Ce qui sur la façon de rendre votre code de travail, je pense que @Rob réponse couvre qu'une partie.

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