2009-10-20 10 views
21

tengo un método con la siguiente firma:escribir un método que acepta una expresión lambda

void MyMethod(Delegate d){}; 
void MyMethod(Expression exp){}; 
void MyMethod(object obj){}; 

Sin embargo, esto no funciona para compilar:

MyMethod((int a) => a) 

con el siguiente error:

"Cannot convert lambda expression to type 'object' because it is not a delegate type" 

¿Por qué no funciona?

Editar: Sé que esto funciona. El compilador compila la expresión lambda a un delgate en este caso, creo.

void MyMethod(Func<int, int> d){}; 

Saludos cordiales,

+3

Fyi, cuando algo no se puede compilar, lea (y publique) el mensaje de error también. –

+0

@SharePoint Novato: por favor vea mi publicación actualizada. Eso debería resolver tu error ahora. – Noldorin

Respuesta

17

Porque el tipo System.Delegate no es un "Delegado". Es solo la clase base. Tienes que usar un tipo de delegado con la firma correcta. Definir su método de la siguiente manera:

void MyMethod(Func<int, int> objFunc) 

EDIT:

MiMetodo (objeto) no funciona debido a una expresión lambda tiene ningún tipo en su propio, pero el tipo se infiere a partir del tipo de la ubicación, está asignado a. Entonces el objeto tampoco funciona. TIENE que usar un tipo de delegado con la firma correcta.

+0

Sé que esto funciona, quiero saber por qué las otras firmas no funcionan. –

+0

Luego lea lo que he escrito. System.Delegate NO es un delegado, una expresión lambda no se puede convertir a System.Delegate. De hecho, las expresiones lambda no tienen ningún tipo en sí mismas, obtienen su tipo del tipo de la variable a la que están asignadas, por lo que el objeto tampoco funciona. –

+0

Pero estoy especificando el tipo yo mismo. No se está deduciendo implícitamente, "(int a) => a;". ¿Podría explicar, por favor, que las expresiones lambda no tienen ningún tipo en sí? –

12
void MyMethod(Action<int> lambdaHereLol) 
{ 
    lambdaHereLol(2); 
} 

en uso:

var hurrDurr = 5; 
MyMethod(x => Console.Write(x * hurrDurr)); 

C# es un lenguaje de tipos estáticos. El compilador necesita saber el tipo de todo lo que trata. Las lambdas son un poco difíciles de identificar, y algunas veces el compilador no puede resolverlo. En mi ejemplo anterior, si MyMethod tomó un objeto, el compilador no pudo determinar que x es un int (mi ejemplo es simple, pero no hay nada que diga que no puede ser mucho más complejo y difícil de determinar). Entonces, debo ser más explícito al definir el método que toma mi lambda.

+1

¿Alguien más siempre mezcla "cloture" y "closure"? – Will

+3

+1 para la semántica del codificador Troll – Filip

+1

"C# es un lenguaje estáticamente tipado. El compilador necesita saber el tipo de todo lo que trata". Esto es cierto, pero el resto no sigue. Hay muchos lenguajes estáticos que pueden inferir la firma de tipo para usted. F # es un ejemplo. – rgrinberg

2

Prueba esto:

void MyMethod(Action<int> func) { } 

Es necesario un delegado de tipado fuerte como un parámetro para el método. El motivo por el que las otras llamadas fallan es porque el compilador de C# no le permitirá pasar una expresión lambda a un método que espera un Object porque una expresión lambda no siempre es necesariamente un delegado en todos los casos. Esta misma regla se aplica para pasar la expresión lambda como Delegate.

Cuando pasa el lambda a una función como la que he mostrado anteriormente, la compilación puede asumir con seguridad que desea que la expresión lambda se convierta a un tipo de delegado específico y lo hace.

+0

Sé que esto funciona, quiero saber por qué las otras firmas no funcionan. –

0

Es simplemente la naturaleza del compilador que necesita para emitir explícitamente un objeto delegado a Delegate al pasarlo como un parámetro de tipo Delegate. De hecho, las expresiones lambda complican las cosas aún más en el sentido de que no son implícitamente convertibles para los delegados en este caso.

Lo que se necesita es un molde doble, como por ejemplo:

MyMethod((Delegate)(Func<int, int>)((int a) => a)); 

que por supuesto se corresponde con la firma del método:

void MyMethod(Delegate d); 

Dependiendo de su situación, es posible que desee definir un parámetro del tipo Func<int> en lugar de Delegate (aunque dudaría en agregar una sobrecarga, solo porque agrega complejidad innecesaria en la honestidad).

+0

Esto tampoco funciona. –

+0

Sí, porque, como ya he escrito, System.Delegate NO es un delegado. Es solo el tipo de base para los delegados. –

+1

Woops, tienes razón. Me perdí la conversión a un tipo de delegado que debe ocurrir primero. Este tipo de información da una idea de cómo el compilador de C# realiza la conversión implícita de las expresiones lambda a los delegados, y cómo luego lo pasa como un parámetro. Lo que ves aquí es hacer todo "manualmente". – Noldorin

3

Un lambda como (int a) => a se ajustan a cualquier delegado que toma un int y devuelve un int. Func<int,int> es solo un ejemplo, y puede declarar uno fácilmente con delegate int Foo(int x);. De hecho, esta expresión lambda incluso se ajusta a un delegado que toma un int y devuelve un double, porque el resultado de la lambda (a) es implícitamente convertible a double.

Para que una lambda se pueda asignar a todos los tipos de delegados que caben, la lambda en sí misma no tiene un tipo. En cambio, toma el tipo de delegado en el que lo está usando, siempre que sea posible. ((int a) => a no se pueden asignar a Func<byte, byte> por supuesto.)

Mientras tanto Func<int, int> y el delegado Foo he definido por supuesto, puede ser convertido en Delegate, un lambda no se puede convertir directamente a Delegate porque no está claro cuál es su real la firma sería entonces. Después de Delegate d = (int a) => a, ¿d sería Foo, o Func<int, int>, o incluso Func<int, double>? Todas son posibilidades válidas, y el compilador no tiene idea de lo que pretendía. Podría hacer una mejor suposición, pero C# no es el tipo de lenguaje que hace ese tipo de conjeturas. Esta es también la razón por la que no puede hacer algo como var = (int a) => a.

creo que el mensaje de error que el compilador da para Delegate d = (int a) => a; es muy claro:

Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type

Intuitivamente se podría pensar Delegate es un tipo de delegado, pero no es así como funcionan las cosas. :)

0

El motivo por el que falla es la misma razón por la que una expresión como "objeto del = (int a) => a" o incluso "var del = (int a) => a" falla. Podría pensar que el compilador podría averiguar el tipo de su expresión lambda dado que explícitamente proporciona el tipo del argumento, pero incluso sabiendo que la expresión toma un int y devuelve un int, hay una cantidad de tipos de delegados que podrían convertirse. a. El tipo de delegado Func es el más utilizado para funciones genéricas como esta, pero eso es solo una convención y nada que el compilador sepa.

Lo que debe hacer es convertir la expresión lambda en un tipo de delegado concreto para que el compilador seleccione la sobrecarga de delegado, ya sea utilizando la sintaxis de molde normal (Func) ((int a) => a), o utilizando la sintaxis del constructor delegado new Func ((int a) => a).

Además, normalmente no desea utilizar la clase delegada sin tipo a menos que necesite invocar algo diferente según el número de argumentos que acepta. Casi siempre es mejor aceptar un Func o una Acción para cosas como devoluciones de llamada.

Cuestiones relacionadas