A debugger mellékhatásai

Nagy, átláthatatlan alkalmazás belső állapotának felderítését megönnyítheti egy jó debugger. A Visual Studio debuggere kifejezetten a könnyen használható kategóriában van, de ez a gyakori előny néha kegyetlenül meg tudja keseríteni az ember életét.

Tegyük fel, hogy munkánk során egy idegen dll-lel kell dolgoznunk (azért idegen, mert hibás), melyben található egy Adat nevű osztály:

    public class Adat
    {
        public byte[] Content
        public int ContentLength
    }

Állítólag egy adatbázisból töltöget be tartalmat amikor szükséges. Elég egyszerű: van egy Content tulajdonság a tartalom elérésére és egy ContentLength tulajdonság a tartalom méretének lekérdezésére.

Próbáljuk ki. Mondjuk megpéldányosítom és kiírom az adat hosszát:

    static void Main( string[] args )
    {
        try
        {
            Adat adat = new Adat();
            // ...
            Console.WriteLine( "A tartalom hossza:" );
            Console.WriteLine( adat.ContentLength );
        }
        catch( Exception e )
        {
            Console.Error.WriteLine( e );
        }

        Console.ReadLine();
    }

Nem túl bonyolult kód, elsőre futnia kéne, nyomom az F5-öt:

debug1

Elszállt az alkalmazás, állítólag a 16. sorban. Akkor nézzük meg melyik is a 16. sor?

            Console.WriteLine( adat.ContentLength );

Ez nem egy bonyolult sor, valószínűleg az adat lesz null, vagy az adat.ContentLength repül el. Semmi gond, megnézhetem a Visual Studio debuggerével: beteszek egy töréspontot, és megnézem a watch ablakban, hogy mi a null.

debug2

Azt írja, hogy az adat szépen példányosítva van, az adat.ContentLength pedig 3. Akkor inkább továbbengeden, fusson le nyugodtan.

debug3

Kicsit kellemetlen, de ezúttal lefutott. Megismétlem még néhányszor a fenti lépéseket, és az eredmény mindig ugyanaz: Törésponttal, watch ablakkal nincs hiba, egyébként NullReferenceException.

A fenti példán érezni, hogy csak most találtam ki. Mert nem merem megmondani, hogy milyen fejlesztés közben jött elő. Akkor rengeteg forráskódunk volt, és valami magic-re gyanakodtunk (tudod: amikor páratlanszor mész át a hídon, de mégis mindig ugyanazon az oldalon ébredsz), végül elkértük az 'Adat' osztály forrákódját:

    public class Adat
    {
        private byte[] _content;

        public byte[] Content
        {
            get
            {
                if( this._content == null )
                    this._content = LoadContent();
                return this._content;
            }
        }

        public int ContentLength
        {
            get
            {
                return this._content.Length;
            }
        }

        private byte[] LoadContent()
        {
            // tartalom betoltese peldaul adatbazisbol
            return new byte[] { 0x00, 0x00, 0x00 };
        }
    }

Áhá. Szóval ez egy lusta betöltést implementáló őrület. Van a _content tagváltozó, aminek akkor töltik fel a tartalmát, ha megnézed a Content tulajdonságot. Ez egész jó ötlet, különösen, hogy a _content private. Tehát a tartalom elérésekor nem lehet probléma.

De mi a helyzet a ContentLength tulajdonsággal? Ez közvetlenül a _content tagváltozó Length tulajdonságával tér vissza. Amikor a _content nem null! Nézzük csak vissza a programomat:

            Adat adat = new Adat();
            // ...
            Console.WriteLine( "A tartalom hossza:" );
            Console.WriteLine( adat.ContentLength );

Szóval úgy akartam kiírni a tartalom hosszát, hogy előtte nem nyúltam hozzá a Content tulajdonsághoz, tehát az Adat példányban a _content inicializálatlan volt, és NullReferenceException-nel elszállt.

Szuper, de akkor miért nem jött elő a hiba Visual Studio debug + watch kombinációval? Mert a watch ablakban megnéztük az adat változót, és a tulajdonságait:

debug4

Ahhoz, hogy a Studio ki tudja írni a ContentLength tulajdonság értékét meghívta a hozzátartozó gettert. Így persze példányosodott a _content tagváltozó, és máris nem volt mi NullReferenceException-t dobjon. (Az ábrán még azt is meg lehet figyelni, hogy amikor a _content tagváltozó értékét írta ki, amihez nem kellett meghívni a gettert akkor a _content még null volt.)

Elég vicces, de az az igazság, hogy debuggolással olyan állapotba vihetjük a megfigyelt alkalmazásunkat, ahová az debugger nélkül sosem jutna el! Ebben az esetben ez azért történt meg, meg olyan tulajdonság értékére voltunk kíváncsiak, amelyhez tartozó getternek mellékhatása is volt.



Dávid Zoltán

Dávid Zoltán Mérnök Informatikusként végeztem a BME-n, jelenleg webfejlesztéssel és gépi tanulással foglalkozom.

2007.07.02. 21:41:12 | Permalink | Hozzászólások: 0 | Tárgyszavak: , ,


  • Windows Phone: platform reset

    Balássy György (MS RD, ASP.NET MVP, MCTS) Olvasgatom a híreket és a MIX környéki cikkeket az új Windows Phone 7-ről, és közben eszembe jutott egy régi mondás: “Sikeres üzletmenet láttán mindig gondolj arra, hogy valaki egyszer lépett egy merészet!”. Úgy látszik, végre felismerték Redmondban, hogy elment a mobil vonat az orruk előtt és a korábbi Windows Mobile foltozgatásával soha nem fogják utolérni. Nem maradt más hátra, mint nulláról építkezni, ami egy ilyen gyorsan változó piacon igen merész lépés. Tovább »
  • WSS Gatherer Error 2424

    Balássy György (MS RD, ASP.NET MVP, MCTS) Nálam MOSS-on, másnál WSS-en jelentkezett az alábbi hiba keresésnél. Tovább »


Írja meg Ön is véleményét!


Hozzászólásokat csak regisztrált, bejelentkezett felhasználóktól tudunk elfogadni!

Hozzászólások