2012-01-03 44 views
8

Debajo de una función Compose. Si f y g son funciones únicas que devuelven valores, entonces Compose(f,g) devuelve una función que al llamar al x realiza el equivalente a f(g(x)).Composición de la función

static Func<X, Z> Compose<Z, Y, X>(Func<Y, Z> f,Func<X, Y> g) 
{ return x => f(g(x)); } 

Aquí hay un par de simples Func valores que puede estar compuesto:

Func<int, bool> is_zero = x => { return x == 0; }; 

Func<int, int> mod_by_2 = x => { return x % 2; }; 

P. ej esto funciona:

Console.WriteLine(Compose(is_zero, mod_by_2)(4)); 

Sin embargo, si en lugar de tener estos métodos estáticos equivalentes:

static bool IsZero(int n) { return n == 0; } 

static int ModBy2(int n) { return n % 2; } 

el mismo ejemplo no funciona con ellos. Es decir. esto produce un error de tiempo de compilación:

Console.WriteLine(Compose(IsZero, ModBy2)(4)); 

tipos explícitamente que pasan a Compose corrige el problema:

Console.WriteLine(Compose<bool, int, int>(IsZero, ModBy2)(4)); 

¿Hay alguna forma de escribir Compose tal que funciona en los métodos estáticos sin los tipos explícitos?

¿Es este un buen enfoque para implementar Compose? ¿Alguien puede hacer mejoras a esto?

+0

rigidez C# 's en su tratamiento de los tipos de función/delegado es una cosa que siempre me ha frustrado cuando se compara con el pato -tipo de idiomas como JavaScript. –

Respuesta

10

El problema aquí no es el uso de métodos static sino el uso de grupos de métodos. Cuando utiliza un nombre de función como expresión sin invocarlo, se trata de un grupo de métodos y debe pasar por la conversión de grupos de métodos. Tendría exactamente el mismo problema con los métodos de instancia.

El problema con el que se está encontrando es que C# no puede hacer la inferencia del tipo de retorno en los grupos de métodos. El uso de Compose(IsZero, ModBy2)) requiere que se infiera el tipo de retorno para IsZero y ModBy2 y, por lo tanto, esta operación falla.

Esta es una limitación conocida en las capacidades de inferencia del compilador de C#. Eric Lippert escribió un extenso artículo de blog sobre este tema en particular que cubre este problema en detalle

+4

Observo que el artículo está un poco desactualizado. En este caso particular, es correcto; no se puede hacer inferencia sobre los grupos de métodos porque tenemos un problema de huevo y gallina; no podemos determinar cuáles son los tipos de delegados hasta que sepamos qué método se elige del grupo de métodos, y no podemos hacer una resolución de sobrecarga en un grupo de métodos hasta que sepamos los tipos delegate * formal parameter *. Si, por el contrario, los delegados * return types * se inferían pero los * tipos de parámetros formales * se conocían de alguna manera, la inferencia del tipo de retorno funcionaría en los grupos de métodos. –

+0

_Cuando se utiliza un nombre de función como expresión sin invocarlo, se trata de un grupo de métodos y debe pasar por la conversión del grupo de métodos. Aceptar. Entonces, el nombre 'IsZero' se refiere a un grupo de métodos. Pero en este caso, hay claramente un solo método en el grupo, y por lo tanto no hay ambigüedad sobre el tipo de devolución.Obviamente, el equipo del compilador decidió no aprovechar estos casos (grupos de método único). ¿Hubiera sido incorrecto hacer lo contrario? (Avíseme si debo abrir una pregunta separada para esto ... :-)) – dharmatech

+0

Por supuesto, otro caso no ambiguo sería grupos de métodos donde todos los métodos en el grupo tienen el mismo tipo de devolución. – dharmatech

Cuestiones relacionadas