'Delegato 'System.Action' non prende 0 argomenti.' Si tratta di un compilatore C # bug (lambda + due progetti)?

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

Domanda

Si consideri il codice qui sotto. Assomiglia perfettamente valido C # codice giusto?

//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
                            };
    }
}

ottengo un errore del compilatore 'Delegato 'Azione' non prende 0 argomenti.' alla posizione indicata con il (Microsoft) C # 4.0 del compilatore. Si noti che si deve dichiarare ActionSurrogate in un progetto diverso per questo errore manifesto.

Si fa più interessante:

// 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 */ };
    }
}

Ti ho inciampare su un bug compilatore C # qui?

Si noti che questo è un bug piuttosto fastidioso per chi ama utilizzando lambda molto e sta cercando di creare una libreria di strutture di dati per uso futuro ... (me)

EDIT:. Rimosso caso difettoso:

I copiato e messo a nudo il mio progetto originale al minimo per rendere questo accada. Questo è letteralmente tutto il codice nel mio nuovo progetto.

È stato utile?

Soluzione

Questo è probabilmente un problema con il tipo inferance, apperently le deduce compilatore a come Action<T> invece di Action (si potrebbe pensare a è ActionSurrogate, che si adatterebbe la firma Action<Action>>). Prova a specificare il tipo di a esplicitamente:

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

Se questo non è il caso -. Potrebbe controllare in giro il vostro progetto per le auto definito delegati Action che prendono un parametro

Altri suggerimenti

AGGIORNAMENTO FINALE:

Il bug è stato corretto in C # 5. Ci scusiamo ancora una volta per l'inconveniente, e grazie per il rapporto.


Analisi originale:

I in grado di riprodurre il problema con la linea di comando del compilatore. Sembra certo come un insetto. E 'probabilmente colpa mia; dispiace. (Ho scritto tutto il codice di controllo di conversione lambda-to-delegato.)

Sono in un caffè in questo momento e non ho accesso alle fonti del compilatore da qui. Cercherò di trovare un po 'di tempo per riprodurre questo nella build di debug domani e vedere se riesco a capire che cosa sta succedendo. Se non trovo il tempo, sarò fuori ufficio fino a dopo Natale.

La tua osservazione che l'introduzione di una variabile di tipo azione fa sì che il problema a scomparire è estremamente interessante. Il compilatore mantiene molte cache sia per motivi di prestazioni e per l'analisi richiesta dal specifica del linguaggio. Lambda e le variabili locali, in particolare, hanno un sacco di logica caching complesso. Sarei pronto a scommettere tanto quanto un dollaro che alcuni cache è in fase di inizializzazione o compilato male qui, e che l'uso dei riempimenti delle variabili locali nel giusto valore nella cache.

Grazie per la segnalazione!

UPDATE: ora sono sul bus ed è venuto solo per me; Credo di sapere esattamente ciò che è sbagliato. Il compilatore è pigro , in particolare quando si tratta di tipi che è venuto dai metadati. Il motivo è che ci potrebbero essere centinaia di migliaia di tipi nelle assemblee di riferimento e non v'è alcuna necessità di informazioni di carico di tutti loro. Hai intenzione di utilizzare molto meno dell'1% di loro probabilmente, quindi cerchiamo di non sprecare un sacco di tempo e roba memoria di caricamento non si è mai intenzione di utilizzare. Infatti la pigrizia va più in profondità; un tipo passa attraverso diversi "stadi" prima di poter essere utilizzato. In primo luogo il suo nome è noto, allora il suo tipo di base, quindi se la sua gerarchia di tipo di base è ben fondata (aciclico, ecc), allora i suoi vincoli dei parametri tipo, poi i suoi membri, quindi se i membri sono ben fondate (che sostituisce esclusione qualcosa della stessa firma, e così via.) Scommetto che la logica di conversione non riesce a chiamare il metodo che dice "assicurarsi che i tipi di tutti i parametri delegati hanno loro i membri noti", prima di controlla la firma del invoke delegato per la compatibilità. Ma il codice che rende una variabile locale probabilmente non farlo. Penso che durante il controllo di conversione, il tipo di azione potrebbe anche non avere un metodo Invoke per quanto riguarda il compilatore è interessato.

Lo scopriremo a breve.

UPDATE: I miei poteri psichici sono forti questa mattina. Quando i tentativi di risoluzione di sovraccarico per determinare se esiste un metodo "Invoke" del tipo delegato che prende zero argomenti, che trova a zero metodi Invoke da scegliere . Dovremmo assicurando che i metadati tipo delegato è completamente caricato prima di noi la risoluzione di sovraccarico. Che strano che questo è passato inosservato questo lungo; si Repros in C # 3.0. Naturalmente non repro in C # 2.0, semplicemente perché non c'erano lambda; i metodi anonimi in C # 2.0 richiedono di indicare il tipo in modo esplicito, che crea un locale, che, come sappiamo carichi i metadati. Ma immagino che la causa principale del bug - che la risoluzione di sovraccarico non forza i metadati di caricamento del invoke -. Risale al C # 1.0

In ogni caso, affascinante bug, grazie per il report. Ovviamente hai una soluzione alternativa. Dovrò QA pista da qui e cercheremo di farlo riparare per C # 5. (abbiamo perso la finestra per service Pack 1, che è già in beta .)

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


        }

Questo compilerà. Alcuni problema tecnico con il compilatore suo grado di trovare il delegato d'azione senza parametri. È per questo che stanno ottenendo l'errore.

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>();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top