42 votos

'Delegado 'Del Sistema.La acción' no toma 0 argumentos.' Es este un compilador de C# error (lambdas + dos proyectos)?

Considere el siguiente código. Parece perfectamente válido de C# el código de derecho?

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

Obtengo un error del compilador 'Delegado 'Acción' no toma 0 argumentos.' en la posición indicada mediante el (Microsoft) C# 4.0 compilador. Tenga en cuenta que usted tiene que declarar ActionSurrogate en un proyecto diferente para este error manifiesto.

Se pone más interesante:

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

Hice tropezar con un compilador de C# error aquí?

Tenga en cuenta que esta es una muy molesto error para alguien que le gusta el uso de expresiones lambda mucho y está tratando de crear una de las estructuras de datos de la biblioteca para uso futuro... (me)

EDIT: se ha eliminado erronous caso.

He copiado y despojado de mi proyecto original hasta el mínimo para hacer que esto suceda. Esto es, literalmente, todo el código en mi nuevo proyecto.

66voto

Eric Lippert Puntos 300275

ÚLTIMA ACTUALIZACIÓN:

El error ha sido corregido en C# 5. Disculpas de nuevo por las molestias, y gracias por el informe.


Análisis Original:

Puedo reproducir el problema con el compilador de línea de comandos. Ciertamente parece un error. Probablemente sea culpa mía, lo siento. (He escrito a todos los de la lambda-a-delegado de conversión de código de comprobación.)

Estoy en un café ahora mismo y no tengo acceso a el compilador de fuentes a partir de aquí. Voy a tratar de encontrar algo de tiempo para reproducir este en la generación de depuración mañana y ver si puedo trabajar de lo que está pasando. Si no encuentro el momento, estaré fuera de la oficina hasta después de Navidad.

Su observación de que la introducción de una variable de tipo Acción hace que el problema desaparezca es muy interesante. El compilador mantiene muchos de caché para ambos motivos de rendimiento y para el análisis requerido por la especificación del lenguaje. Las Lambdas y las variables locales, en particular, tienen un montón de complejos de almacenamiento en caché de la lógica. Yo estaría dispuesto a apostar tanto como un dólar que algunos de caché se inicializa o llenado de mal aquí, y que el uso de la variable local se llena en el valor correcto en la memoria caché.

Gracias por el informe!

ACTUALIZACIÓN: ahora estoy en el bus y que sólo vino a mí; creo que sé exactamente lo que está mal. El compilador es perezoso, especialmente cuando se trabaja con tipos que vinieron de metadatos. La razón es que no podría ser de cientos de miles de tipos en los ensamblados de referencia y no hay necesidad de cargar la información acerca de todos ellos. Vas a utilizar mucho menos de 1% de ellos, probablemente, por lo que no vamos a perder una gran cantidad de memoria y tiempo de carga de cosas que nunca vas a utilizar. De hecho, la pereza va más allá de eso, un tipo que pasa a través de varias "etapas" antes de ser utilizado. La primera se conoce su nombre, su tipo base, si su tipo base de la jerarquía está bien fundada (acíclicos, etc), su parámetro de tipo de restricciones, sus miembros, entonces si los miembros están bien fundadas (que reemplaza reemplazar algo de la misma firma, y así sucesivamente.) Apuesto a que la lógica de conversión es no llamar al método que dice "asegúrese de que los tipos de todo el delegado de los parámetros de sus miembros se conocen", antes de que se comprueba la firma del delegado invocar para la compatibilidad. Pero el código que hace que una variable local probablemente no hacerlo. Creo que durante la conversión de cheques, el tipo de Acción puede incluso no tener un método invoke tan lejos como el compilador de que se trate.

Lo sabremos en breve.

ACTUALIZACIÓN: Mis poderes psíquicos son fuertes de esta mañana. Cuando la sobrecarga de los intentos de resolución para determinar si hay una "Invocar" método del tipo de delegado que tiene de cero argumentos, se encuentra con cero Invocar métodos para elegir. Debemos asegurar que el tipo de delegado de metadatos está completamente cargada antes de hacer la resolución de sobrecarga. Qué extraño que esto ha pasado desapercibido este largo; repros en C# 3.0. Por supuesto que no repro en C# 2.0, simplemente porque no había lambdas; los métodos anónimos en C# 2.0 requieren que el estado explícitamente el tipo, lo que crea un local, que sabemos que las cargas de los metadatos. Pero me imagino que la causa raíz del error - que la resolución de sobrecarga no forzar la carga de metadatos para la invoke - se remonta a C# 1.0.

De todos modos, fascinante error, gracias por el informe. Obviamente tenemos una solución. Voy a tener QA pista de aquí y vamos a tratar de arreglarlo para C# 5. (Hemos perdido la ventana para Service Pack 1, que ya está en beta.)

23voto

Femaref Puntos 41959

Esto probablemente es un problema con el tipo de inferance, el apperently el compilador deduce a como Action<T> en lugar de Action (se podría pensar a es ActionSurrogate, que se ajuste a las Action<Action>> de la firma). Intente especificar el tipo de a de forma explícita:

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

Si este no es el caso - podría comprobar alrededor de su proyecto para que cualquier auto definen Action delegados de tomar un parámetro.

2voto

Amit Bagga Puntos 442
    public static void ThisWontCompile()
        {
            ActionSurrogate b = (Action a) =>
            {
                a();
            };


        }

Se va a compilar. Algún problemilla con el compilador sus incapaz de encontrar la Acción de delegar sin parámetros. Es por eso que usted está recibiendo el error.

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>();

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X