Por qué Task no devuelve nulo

estoy trabajando con lo nuevo Biblioteca de tareas paralelas y hoy fui a este caso:

Este código no se compila:

    internal Task<Guid?> SavePages(string[] pages)
    {
        return Task.Run(() =>
        {
            if (pages == null || pages.Length == 0)
                return null;

            ....

A menos que devuelva explícitamente un Guid anulable nulo:

    internal Task<Guid?> SavePages(string[] pages)
    {
        return Task.Run(() =>
        {
            if (pages == null || pages.Length == 0)
                return (Guid?)null;

            // Check documents path access

¿Por qué este comportamiento, estoy haciendo algo mal? Quiero decir, consigo que el código funcione con la segunda opción, pero no sé si estoy haciendo un mal uso de la biblioteca, quiero decir, nulo siempre es nulo, ¿no es así?

Error de compilación:

No se puede convertir la expresión lambda en el tipo de delegado 'System.Func' porque algunos de los tipos de valor devuelto en el bloque no se pueden convertir implícitamente al tipo de valor devuelto del delegado

http://msdn.microsoft.com/en-us/library/dd460717.aspx

preguntado el 01 de julio de 12 a las 20:07

FFR, incluido el error exacto del compilador, sería útil, ya que en este caso probablemente menciona algo sobre no poder inferir el tipo. -

El problema es que nulo siempre es nulo, por lo que el compilador no tiene forma de saber qué tipo está representando con su expresión nula:

Nota al margen: otras dos formas de expresar lo mismo que (Guid?)null se encuentran las new Guid?() y default(Guid?). -

2 Respuestas

Esto tiene que ver con la forma en que el compilador determina el tipo de su lambda. Cuando regresas un llano null, lo único que el compilador puede implicar es que está devolviendo un objeto. Por lo tanto, su lambda sin parámetros es compatible con Task<object>. Sin embargo, la firma de su función dice que está regresando Task<Guid?>, por lo que el tipo de retorno que el compilador implica de su código no es compatible. Cuando lanzas eso null a Guid?, proporciona al compilador la pista que falta para hacer que la lambda sea Task<Guid?>.

Respondido 01 Jul 12, 20:07

El compilador realmente debería deducir esto por sí mismo (aunque está bastante claro por qué no lo hace): zmbq

@zmbq, ¿cómo determinaría qué tipo null ¿posee? es un valor válido para todos los tipos de referencia, por lo que no hay forma de que el compilador pueda determinar el tipo de esa expresión (a cualquier otra cosa que no sea un objeto) - Runa FS

@RuneFS Dado que es un tipo anulable, la asignación debe funcionar, ¿no está de acuerdo? - Erre Efe

El tipo se puede deducir del tipo de SavePages. - zmbq

@zmbq No es así como funciona la inferencia de tipos en C#. Por lo general, el tipo de cada expresión se infiere de forma independiente (aunque las lambdas son una excepción). - svick

Esta es una limitación con la inferencia de tipos en el compilador de C#. Este problema no es ajeno al que involucra al operador ternario:

int? num = a != null ? a.Value : null;         // Will not compile
int? num = a != null ? a.Value : (int?)null;   // Compiles
int? num = a != null ? (int?)a.Value : null;   // Compiles

Otra solución para su situación específica es especificar el tipo genérico explícitamente:

return Task.Run<Guid?>(() =>
{
    if (pages == null || pages.Length == 0)
        return null;

Respondido 01 Jul 12, 22:07

+1 Douglas por la explicación. Una pregunta: ¿El uso de la versión no genérica de Task.Run (como sugiere) disminuye el rendimiento de alguna manera posible? - Erre Efe

a menos que me esté perdiendo algo, solo está hablando de no hacer que el compilador infiera el tipo; de cualquier manera, obtienes el mismo IL AFAICT generado. james manning

@RandolfRincón-Fadul: Mi código todavía usa la versión genérica de Task.Run; de hecho, lo hace explícitamente, en lugar de inferir el tipo (como lo hace su código). Como dijo James Manning, no creo que haya ninguna diferencia en la IL generada, ya que la inferencia de tipo se realiza en tiempo de compilación. Especificar el tipo genérico explícitamente podría hacer que la compilación (no tiempo de ejecución) marginalmente más rápido, pero esto es algo que casi con certeza nunca tendrá que considerar. - Douglas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.