An Avanade Blogging Community

Welcome to An Avanade Blogging Community Sign in | Join | Help
in Search

vtortola

El bug de String.IsNullOrEmpty en .NET 2.0

Leo en un blog de MSMVPs.com una noticia de hace más de un año que contaba que se detecto un bug en el método estático String.IsNullOrEmpty causado por una mala optimización del JIT que puede disparar un NullReferenceException cuando se ejecuta en un ensamblado compilado en Release fuera del IDE, es decir, nuestras aplicaciones en producción. Si se ejecuta en modo Debug ó en Release dentro de IDE (donde se ejecuta sin optimizaciones) el bug no se reproduce. A día de hoy, el bug sigue existiendo.

El bug, no deja de ser bastante curioso y nos da una idea de lo lejos que está nuestro código C# de lo que verdareramente se produce. Lamentablemente Microsoft dijo que se solucionaria en Orcas... pero no sacó parche ni actualización para .NET 2.0, lo cual a mi personalmente me repatea un poco, pués una de las ventajas del .NET Framework es que MS se encarga de actualizarlo en caso de problemas de seguridad ó bugs de forma trasnparente para nuestras aplicaciones, pero así... tener que migrar a una tecnología beta... no es la solución. Aún recuerdo cuando me topé con el bug del atributo OneWay en los métodos invocados asíncronamente... otro bug que sigue igual, sin arreglar en 2.0 y solventado en Orcas. Lo que no se es si realmente se arregló en Orcas ... ó simplemente es que en Orcas no sucede :P

El bug es fácil de reproducir en un principio, basta con este código:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace BugTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("starting");
            test(null);
            Console.WriteLine("finished");
            Console.ReadLine();
        }
 
        static void test(string x)
        {
            for (int j = 0; j < 10; j++)
            {
                if (String.IsNullOrEmpty(x))
                {
                    //TODO:
                }
            }
        }
    }
}

Ahora selecciona modo Release, construye y ejecuta fuera del IDE con Ctrl+F5 ... tachann!!

bugstringempty

starting

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at BugTest.Program.test(String x) in C:\Documents and Settings\Valeriano\Mis documentos\Visual Studio 2005\Projects\BugTest\BugTest\Program.cs
:line 19
   at BugTest.Program.Main(String[] args) in C:\Documents and Settings\Valeriano\Mis documentos\Visual Studio 2005\Projects\BugTest\BugTest\Progr
am.cs:line 12
Presione una tecla para continuar . . .

Igualmente sucede dentro de un bucle while, se puede ver en otra simple función:

        static void test2(string x)
        {
            int count=0;
            while (count < 10)
            {
                if (String.IsNullOrEmpty(x))
                {
                    //TODO:
                }
                count++;
            }
        }

Pero cuando hago una simple modificación como añadir una línea para mostrar en que iteración sucede la excepción ... se ejecuta correctamente:

        static void test(string x)
        {
            for (int j = 0; j < 10; j++)
            {
                Console.WriteLine(j.ToString());
                if (String.IsNullOrEmpty(x))
                {
                    //TODO:
                }
            }
        }

Del mismo modo si altero 'test2' con una simple línea se ejecuta correctamente sin que se dispare la excepción:

        static void test2(string x)
        {
            int count=0;
            while (count < 10)
            {
                if (String.IsNullOrEmpty(x))
                {
                    //TODO:
                }
                count++;
                Console.WriteLine(count.ToString());
            }
        }

Para más 'inri' si intento capturar la excepción dentro del loop no se dispara :

        static void test(string x)
        {
            for (int j = 0; j < 10; j++)
            {
                try
                {
                    if (String.IsNullOrEmpty(x))
                    {
                        //TODO:
                    }
                }
                catch (NullReferenceException nEx)
                {
                    Console.WriteLine("Capturada {0}",nEx.GetType().Name);
                    throw;
                }
            }
        }

Ya paranoíco, re-compruebo comentando las nuevas líneas para asegurarme... y se dispara la excepción de nuevo:

        static void test(string x)
        {
            for (int j = 0; j < 10; j++)
            {
                //try
                //{
                    if (String.IsNullOrEmpty(x))
                    {
                        //TODO:
                    }
                //}
                //catch (NullReferenceException nEx)
                //{
                //    Console.WriteLine("Capturada {0}",nEx.GetType().Name);
                //    throw;
                //}
            }
        }

Ya para crear más incertidumbre sobre el tema (xD), en los comentarios del reporte del bug se indica que intentando crear un sustitutivo ha conseguido reproducir el mismo error. Por ejemplo, creando una clase con un método similar:

/// <summary>
/// Clase para intentar imitar la funcionalidad
/// de String.IsNullOrEmpty()
/// </summary>
class MyUtil
{
    public static bool MyIsNullOrEmpty(string s)
    {
        return s == null || s.Length == 0;
    }
}

Y realizando un test de la misma forma se consigue el mismo resultado, que se dispare un NullReferenceException:

        static void test3(string x)
        {
            for (int j = 0; j < 10; j++)
            {
                if(MyUtil.MyIsNullOrEmpty(x))
                {
                    //TODO:
                }
            }
        }

Así que posiblemente sea un problema relacionado entre la forma en que se cachean las variables para su uso dentro de los 'loop', dicho método de String y sepa Microsoft qué más.

Muchos usuarios sugirieron posibles soluciones y alternativas en el reporte del bug. La recomendación sigue siendo ... "andar con cuidado al usar este método en bucles" :P

Realmente, estando Orcas hace un año en "muy beta"... parece mentira que Microsoft diga que no solventa el error :(

Crossposting desde ElBruno.com
Published Monday, July 30, 2007 9:09 PM by vtortola

Comments

No Comments
Anonymous comments are disabled

About vtortola

MCTS: Windows Applications 2.0 MCTS: Distributed Applications 2.0 MCTS: WebApplications 2.0

This Blog

Post Calendar

<July 2007>
SuMoTuWeThFrSa
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

News

vtortola.NET



Valeriano Tórtola.
MCTS: Windows Apps 2.0
MCTS: Distributed Apps 2.0
MCTS: Web Apps 2.0
Application Developer I.

Syndication