Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.

February 10, 2015 / by Zsolt Soczó

.NET fejtörő 3. – megoldás

Íme a kiírás.

Álljon itt újra a kód is:

struct Counter
{
    int counter;
    public override string ToString()
    {
        return counter++.ToString(CultureInfo.InvariantCulture);
    }
}

[TestMethod]
public void Teaser3()
{
    var sb = new StringBuilder();
            
    var sz = new Counter();
            
    sb.Append(sz);
    Object p = sz;
    Object o = p;
    sb.Append(sz);
    sb.Append(o);
    sb.Append(p);
    sb.Append(o);

    Assert.AreEqual("01234", sb.ToString());
}

Először lemaradt a feladatból a Counter struktúra, anélkül nem volt sok értelme a feladatnak, ezért az első pár választ nyilván félre mehetett.
A feladatban az a trükk, hogy nem class, hanem struktúra a Counter típus. A StringBuilderben a végén 00012 lesz. Miért?

Az sz egy a veremben tárolt struktúra. Az sb.Append() (ebben az esetben) objektumot vár paraméterül (ami reference type), amin meghívja a ToString() metódust, és ennek kimenetét rakja bele az építendő stringbe. Azonban az sz egy struktúra, value type, így azt a CLR-nek fel kell másolnia a heapre. Ezt hívják boxingnak. Ezen pont után a ToString() már a másolaton hívódik meg, az eredeti sz counter mezeje érintetlen marad.
Mivel postfix ++ operátort használunk, a kifejezés kimenete a növelés előtti érték lesz, azaz 0. Ez az első számunk.
Az Object p = sz explicit felboxolja sz-t a heapre. Az Object o = p pusztán egy másik referencia lesz ugyanerre az értékre.
Ezek után sb.Append(sz) újra boxol és kiírja a 0-t, mint a korábbi esetben. Ez a 2. számunk. Sokan szerintem itt már 1-et vártak, ami így is lenne, ha a Counter classként lenne definiálva.
Az sb.Append(o) az eddig még érintetlen felboxolt példányon hívódik meg. Mivel megint csak postfix ++ van, ez is 0 lesz. Ez a 3. 0.
Az sb.Append(p) ugyanezen példányra mutat, így végre ő már megnövelheti a számlálót 2-re, és visszaadhatja az 1-et. Ez a 4. számunk, ami végre 1.
Az sb.Append(o) pedig újra végigjátssza a növelést a más ismerős példánnyal, így jön ki belőle 2.

Ha a típusunk class lett volna, akkor jött volna ki belőle 01234.

A példának látszólag nincs jelentősége, a generikus típusok óta valóban kevesebb baj van a struktúrákkal. De még .NET 1.0-ban láttam olyat, amikor HashTable[“valami”]++ után a x = HashTable[“valami”] változatlan értéket adott vissza, meglepve a kollégákat. Most már talán érthető, miért.

Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.