C# agytorna #01

Szembetalálkoztam egy érdekes kis “hibával”. Igazából a hiba nem jó kifejezés – csak arról van szó, hogy egy kód nagyon nem azt csinálja, amit első ránézésre várna tőle az ember. Az egyik osztálynál előjött bizonyos helyzetekben, a másik osztálynál nem.

Tekintsük az alábbi osztályt:

internal class Alma
{
    public string Szín { get; set; }
}

Majd pedig legyen egy (statikus) almánk a Program osztályban (ami tegyük fel, hogy iszonyat nagy), egy metódusunk, ami elpusztítja és egy, ami naplózza ezt az eseményt. A Main metódusban pedig hozzuk létre az almát, együk meg, logolással, és ezután írjuk ki a színét! (Igen, miután elvileg megettük!)

private static Alma alma = null;

private static string Megesz()
{
    alma = null;
    return "Megettem.";
}

static void Main(string[] args)
{
    alma = new Alma { Szín = "zöld" };
    Log(Megesz());
    Console.WriteLine(alma.Szín);
}

Érezhető, gondolom, hogy ennek a kódnak el kellene szállnia, hiszen a Megesz nullozza az almát, így utána a WriteLine-ban NullReferenceExceptionnek kellene jönnie, amikor megpróbálunk a színéhez hozzáférni.

De nem feltétlenül. 🙂 És ez akkor szép igazán, amikor nem egy ilyen 3 soros kódban, hanem egy 3000 sorosban fordul elő. 🙂

Egészítsétek ki a Program osztályt úgy (vagyis írjátok meg a Log metódust, meg amit szeretnétek), hogy ne dobjon kivételt a kód, hanem szépen kiírja a 3. sor a színt.

Megkötések – hogy elkerüljük a nyilvánvalót:

– Sem a Megesz, sem a Main metódushoz nem nyúlhattok.

– Azt, hogy a Lognak mit kell csinálnia, nem szabom meg, akár üres is lehet, teljesen rátok bízom. De semmilyen módon nem adhat értéket az alma objektumnak. (Nem trükközünk, nem hívhat olyan metódust sem, ami értéket adna neki stb.)

– Attribútumok, fordítási direktívák használata tilos.

– Az alma definíciójával való trükközés (factory, singleton, akármi) is azonnali diszkvalifikációt von maga után. De lehet, hogy csak kizárást. :]

Válaszokat kommentbe! 🙂 Néhány nap múlva leírom a megoldást is, de szerintem pár perc/óra gondolkodás után kitalálható a dolog.

…és most vissza kódolni.

 

Update: a megoldást kommentben írtam le, így ha valaki előbb megpróbálna önerőből rájönni a megoldásra, egyelőre ne olvassa el a kommenteket!

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English
Advertisements

~ Szerző: Fülöp Dávid - 2011. július 6..

18 hozzászólás to “C# agytorna #01”

  1. Üdv!

    Hát lehet, hogy jobb lenne, ha bölcsen megvárnám az eredményt, de a feladatott azt hiszem végre hajtottam:

    Szóval csak Property-re átalakítottam.
    Nagyon kíváncsi vagyok mi lesz a megoldás.
    Azért számos kérdés felvett, szóval én lehet a hosszabb utat választanám és megnézném mi miért történik.

    private static Alma _alma = null;
    private static Alma alma
    {
    get
    {
    return _alma;
    }
    set
    {
    if (value == null) return;
    _alma = value;
    }
    }

    private static string Megesz()
    {
    alma = null;
    return “Megettem.”;
    }

    static void Main(string[] args)
    {
    alma = new Alma { Szín = “zöld” };
    Log(Megesz());
    Console.WriteLine(alma.Szín);
    }

    static void Log(String value)
    {
    //TODO: logging
    }

  2. Teljesen jó gondolat, más is ezzel próbálkozott először. 😉 (Illetve azzal, hogy az almának, ha null volt az értéke, adott egy default értéket.)

    De ahogy a kiírásban szerepel, az almához nem nyúlunk, marad egy árva kis mező; sz’al el a kezekkel. :p

  3. static partial void Log(string message);

    ha nem implementálom, akkor nem hívódik meg a Log metódus, de az argumentuma sem értékelődik ki, azaz az alma-t null-ra állító Megesz metódus sem lesz meghíva.

  4. class Program
    {
    private class Obj
    {
    public static Obj ins = null;

    private string color;

    public string Color
    {
    get
    {
    return color;
    }

    set
    {
    if (color != value)
    {
    color = value;
    ins = this;
    }
    }
    }
    }

    private static Obj obj = null;

    private static string Delete()
    {
    obj = null;
    return “Deleted”;
    }

    static void Main(string[] args)
    {
    obj = new Obj { Color = “Red” };
    Log(Delete());
    Console.WriteLine(obj.Color);
    }

    private static void Log(string p)
    {
    Console.WriteLine(p);
    new Thread(new ThreadStart(ThreadWorker)).Start();
    Thread.Sleep(1000);
    }

    private static void ThreadWorker()
    {
    obj = Obj.ins;
    }
    }

  5. Hali!

    Egy kis kiegészítés az előbbi kommentben szereplő kódhoz:

    A változó és metódus neveket megváltoztattam, remélem nem gond.

    Az alapötlet az, hogy egy másik szál ad értéket az almának (obj nálam).

  6. aiyan: a többszálú megoldás nem rossz ötlet, tényleg okozhat versenyhelyzetből adódó hibákat. De itt a ThreadWorker értéket ad (újra) az alma (vagyis obj) referenciának, és a kiírás szerint ez tilos.

  7. Már megint figyelmetlen voltam, vagy csak félre értettem és a Típusra gondoltam, mind1, most akkor kizártak? 🙂

    Akkor második próbálkozásom akkor a típus-t alakítanám át:

    internal struct Alma
    {
    public string Szín { get; set; }

    public static implicit operator Alma(String value)
    {
    return new Alma { Szín = value };
    }
    }

  8. Dehogy zártak ki. 😀
    De ezt a megoldásod nem értem. Mit nyerünk azzal, ha structtá alakítjuk? A Megesz metódus nullozni próbálná a referenciát, és akkor már ott el fog szállni a dolog. (Ill. le se fordul.)

  9. A Log() egy partial method

  10. Próbáld csak ki 🙂

    Annyit érünk el vele, hogy “nullozható” legyen a struct, vagyis lényegében nem fog elszállni NullReferencel, viszont a Szín értéket elveszítjük, mert azt csinálja, hogy az átadott null a Szín property-nek állítja be.

    Ezt a trükköt használja a Nullable is.

    class Program
    {
    private static Alma alma;

    private static string Megesz()
    {
    alma = null;
    return “Megettem.”;
    }

    static void Main(string[] args)
    {
    alma = new Alma { Szín = “zöld” };
    Log(Megesz());
    Console.WriteLine(alma.Szín);
    }

    static void Log(String value)
    {
    //TODO: logging
    }

    }

    internal struct Alma
    {
    public string Szín { get; set; }

    public static implicit operator Alma(String value)
    {
    return new Alma { Szín = value };
    }
    }

  11. De az alma definíciójához nem nyúlunk! 😀 (Amúgy jó ötlet, le a kalappal, jár érte egy fél sör. 🙂 )

  12. Kis állapotjelentés: már született két jó megoldás is; vagyis ugyanaz kétszer. Éjjel publikálom őket, addig is lehet még agyalni. 🙂

  13. Na, akkor most vége a dolognak, publikáltam a két kommentet, amiben megtalálták a megoldást.

    Ahogy kgabor is leírta, a furcsa viselkedés akkor jön elő, ha a metódust partialként definiáljuk, de nem adunk neki konkrét metódustörzset. A metódushívás így látszólag lefordul, gyakorlatilag viszont a compiler eltávolítja, és mivel a Log metódus nem hívódik, a paraméterként átadott metódushívás sem fut le, tehát egész egyszerűen eltűnik a Megesz metódus hívása is a Mainből.

    Várom a levélbombákat és ipari mennyiségű anthraxot. 🙂

    • B+ Dávid, csak találjalak meg 😀

      Pedig mennyire logikus volt, elég nyíltan céloztál arra, hogy a Log szignatúrájában lesz a megoldás, de parciális metódusra pont nem gondoltam, a paraméter típusával próbáltam trükközni.

      Annyira azért nem volt triviális, én pl. soha nem használok partialt-t, valószínűleg nem vagyok vele egyedül.

      Mindegy, ma is tanultam valami újat, várom a következő kört (addig berakom a kispárnám alá a C# referenciát 🙂 ).

    • hát, gratulálok a megfejtőknek, nem tudom, hogy rájöttem volna-e valaha…

  14. Ohhh, ez cseles, gratulálok a megfejtőknek és az agytorna megalkotásához, következő agytornánál biztos ezzel kezdek!

    Spam incoming!! 🙂

  15. Érdekes. Mindenesetre kíváncsi vagyok hány ember használhatja a parciális metódusokat…
    Ja, és a kiírás alapján úgy gondoltam hogy a Log metódust valami módon implementálni kell (talán ezért is nem jöttem rá). Annyira ki volt hegyezve a dolog arra hogy hagyjunk mindent úgy ahogy van, csak a viselkedésen módosítsunk (legalábbis nekem ez jött le).
    Mindegy, nem biztos hogy eszembe jutott volna, ez gondolom abból adódik hogy én se használom és még csak igényem se volt a használatára.

    Mindenesetre érdekelne, ki mit gondol, milyen helyzetben érdemes használni?

    Morzel

  16. Igen, próbáltam kihangsúlyozni, hogy tényleg bármilyen lehet a Log (hogy lehet akár üres is), csak azt nem írhattam le, hogy nem is kell neki törzset írni, mert akkor gyakorlatilag tálcán kínáltam volna a megoldást. 🙂

    Mikor érdemes használni: szerény nyomott véleményem szerint ~soha. Vannak nagyon marginális szituációk, amikor jól jön, de ahogy fent is látható, elég nagy galibát okozhat, ha az egyik fejlesztő használja, a másik meg nincs felkészülve rá. Generált kódhoz tökéletes, fejlesztett kódban meg… Ha épp megold egy problémát, belerakom, de nagyon ritka. De ez csak egy vélemény. 🙂

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

 
%d blogger ezt kedveli: