« Délégué « System.Action » ne prend pas 0 arguments. » Est-ce un C # bug du compilateur (lambdas + deux projets)?

StackOverflow https://stackoverflow.com/questions/4466859

Question

Considérons le code ci-dessous. On dirait parfaitement valide C # droit de code?

//Project B
using System;
public delegate void ActionSurrogate(Action addEvent);
//public delegate void ActionSurrogate2();
// Using ActionSurrogate2 instead of System.Action results in the same error
// Using a dummy parameter (Action<double, int>) results in the same error

// Project A
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) =>
                            {
                                a(); // Error given here
                            };
    }
}

Je reçois une erreur du compilateur Action « délégué « » ne prend pas 0 arguments. » à la position indiquée en utilisant le (Microsoft) C # 4.0 compilateur. Notez que vous devez déclarer ActionSurrogate dans un projet différent pour cette erreur manifeste.

Il devient plus intéressant:

// Project A, File 1
public static class Class1 {
    public static void ThisWontCompile() {
        ActionSurrogate b = (a) => { a(); /* Error given here */ };
        ActionSurrogate c = (a) => { a(); /* Error given here too */ };
        Action d = () => { };
        ActionSurrogate c = (a) => { a(); /* No error is given here */ };
    }
}

Est-ce que je tombe sur un bug du compilateur C # ici?

Notez que ceci est un bug assez ennuyeux pour quelqu'un qui aime l'aide lambdas beaucoup et tente de créer une bibliothèque de structures de données pour une utilisation future ... (moi)

EDIT:. Supprimé cas erronous

Je copié et dépouillé mon projet initial vers le bas au minimum pour y arriver. Ceci est littéralement tout le code dans mon nouveau projet.

Était-ce utile?

La solution

Ceci est probablement un problème avec le type inferance, Apperently le compilateur infère a comme Action<T> au lieu de Action (il pourrait penser a est ActionSurrogate, ce qui correspondrait à la signature de Action<Action>>). Essayez spécifiant le type de a explicitement:

    ActionSurrogate b = (Action a) =>
                        {
                            a();
                        };

Si ce n'est pas le cas -. Peut vérifier autour de votre projet pour les délégués de Action auto défini en prenant un paramètre

Autres conseils

Mise à jour FINAL:

Le bogue a été corrigé en C # 5. Toutes mes excuses à nouveau pour la gêne occasionnée et merci pour le rapport.


Analyse d'origine:

Je peux reproduire le problème avec le compilateur de ligne de commande. Il ressemble certainement un bug. Il est probablement ma faute; Désolé pour ça. (Je l'ai écrit tout le code de vérification de conversion lambda à délégué.)

Je suis dans un café en ce moment et je ne pas avoir accès aux sources du compilateur d'ici. Je vais essayer de trouver un peu de temps pour reproduire cela dans la version de débogage demain et voir si je peux travailler ce qui se passe. Si je ne trouve pas le temps, je serai hors du bureau avant Noël.

Votre observation que l'introduction d'une variable de type action provoque le problème à disparaître est extrêmement intéressant. Le compilateur conserve de nombreuses caches pour des raisons de performance et d'analyse requis par la spécification du langage. Lambdas et variables locales, en particulier, ont beaucoup de logique de mise en cache complexe. Je serais prêt à parier autant qu'un dollar que certains cache est en cours d'initialisation ou rempli de mal ici, et que l'utilisation des remplissages variables locales dans la juste valeur dans le cache.

Merci pour le rapport!

Mise à jour: Je suis maintenant sur le bus et juste est venu me; Je pense que je sais exactement ce qui est faux. Le compilateur est paresseux , en particulier lorsqu'ils traitent avec des types qui venaient de métadonnées. La raison en est qu'il pourrait y avoir des centaines de milliers de types dans les assemblys référencés et il n'y a pas besoin d'informations de charge sur chacun d'eux. Vous allez utiliser beaucoup moins de 1% d'entre eux sans doute, donc il ne faut pas perdre beaucoup de temps et d'autres choses de chargement de mémoire que vous ne réussirez jamais à utiliser. En fait, la paresse est plus profond; un type passe par plusieurs « étapes » avant de pouvoir être utilisé. Tout d'abord son nom est connu, puis son type de base, alors que sa hiérarchie de type de base est bien fondée (acyclique, etc.), puis ses contraintes de paramètres de type, puis ses membres, alors que les membres sont bien fondées (qui outrepasse override quelque chose de la même signature, et ainsi de suite.) Je parie que la logique de conversion ne parvient pas à appeler la méthode qui dit « assurez-vous que les types de tous les paramètres de délégués ont leurs membres connus », avant il vérifie la signature du délégué Invoke pour la compatibilité. Mais le code qui fait une variable locale probablement fait faire. Je pense que lors de la vérification de la conversion, le type d'action pourrait même pas une méthode Invoke pour autant que le compilateur est concerné.

Nous allons découvrir prochainement.

MISE À JOUR: Mes pouvoirs psychiques sont forts ce matin. Lorsque la résolution de surcharge tente de déterminer s'il existe une méthode « Invoke » du type délégué qui prend zéro arguments, il trouve zéro méthodes Invoke à choisir . Nous devrions veiller que les métadonnées de type délégué est entièrement chargé avant de faire la résolution de surcharge. Comme il est étrange que cela est passé inaperçu cette longue; il Repros en C # 3.0. Bien sûr, il ne repro pas en C # 2.0 simplement parce qu'il n'y avait pas lambdas; méthodes anonymes en C # 2.0 vous obliger à indiquer le type explicitement, ce qui crée un local, que nous connaissons charge les métadonnées. Mais je suppose que la cause du bug - que la résolution de surcharge ne force pas les métadonnées de chargement pour l'Invoke -. Remonte à C # 1.0

En tout cas, bug fascinant, merci pour le rapport. Il est évident que vous avez une solution de contournement. Je vais devoir suivre QA d'ici et nous allons essayer de le faire réparer C # 5. (Nous avons manqué la fenêtre pour service Pack 1, qui est déjà en bêta .)

    public static void ThisWontCompile()
        {
            ActionSurrogate b = (Action a) =>
            {
                a();
            };


        }

compilera. Certains pépin avec le compilateur son incapable de trouver le délégué d'action sans paramètres. C'est pourquoi vous obtenez l'erreur.

public delegate void Action();
public delegate void Action<T>();
public delegate void Action<T1,T2>();
public delegate void Action<T1,T2,T3>();
public delegate void Action<T1,T2,T3,T4>();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top