Adott típusú ősvezérlő keresése a vizuális fán

Az elmúlt hetekben elég sok dolog szakadt rám, de a sok munka és vizsga mellett született néhány olyan probléma és olyan megoldás, amit talán érdemes lesz postba foglalnom. (Nem meglepő módon utóbbiaké volt a nehezebb szülés. :))

Továbbra is bőszen tolunk előre egy projektet, aminek kapcsán sikerült egy meglehetősen hasznos metódust összeraknom. Csak néhány sor, és nincs benne semmi világmegváltó, de nekem nagyon hiányzott a WPF-ből.

A probléma, amit meg kell oldania, egyszerű: adott egy vezérlő/vizuális objektum, és ennek kellene megtalálni kódban egy bizonyos típusú ősét, vagyis fellépkedni a vizuális fán addig, amíg nem találunk egy megfelelő típusú vezérlőt, vagy el nem fogy a fa.

Íme a kód:

public static class DependencyObjectExtender
{
    public static T FindAncestor<T>(this DependencyObject obj)
        where T : DependencyObject
    {
        var tmp = VisualTreeHelper.GetParent(obj);
        while (!(tmp is T))
        {
            tmp = VisualTreeHelper.GetParent(tmp);
        }
        return tmp as T;
    }
}

Nem megmondtam, hogy nem bonyolult? 🙂

Igazság szerint a VisualTreeHelpert akartam kiegészíteni, de azt sajnos nem lehet, így a DependencyObjectre rakunk egy bővítőfüggvényt. Szép generikus, a típusparamétere szintén egy DependencyObject leszármazott kell legyen, és egy ugyanilyen típusú objektummal tér majd vissza. Vagy null-lal (nullal?), ha nem találta.

A metódus pedig lekérdezi annak az elemnek az ősét, amin meghívták, majd ellenőrzni, hogy null-e (vagyis nincs ős), illetve, hogy ha nem null, a típusa megegyezik-e a keresett típussal. Ha nem null, de a típus sem stimmel, akkor rekurziót nélkülözve az aktuális “parent” parentjén végzi el ezt az ellenőrzést. 🙂

Végül vagy meglesz az elem, és visszaadja, vagy eléri a fa legtetejét (vagy legalját, denevérek esetén :)), és null-lal tér vissza.

A kód – illetve nem sokkal bonyolultabb, hibakezeléssel kibővített változata – eddig jól működőnek mutatkozott, de természetesen szívesen fogadok minden megjegyzést, hogy hol lehetne tweakelni, illetve hol fordulhatnak elő rejtett problémák.

Holnap(utánután) egy picit továbbvisszük ezt a metódust.

Addig is vissza kódolni.

Update: köszi pacshunak, aki felhívta a figyelmem egy egyszerűsítési lehetőségre, frissítettem a kódot! 🙂

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
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
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
Reklámok

Generikus metódus hívása futásidejű típusparaméterrel

//NOTE: a devportal OneTime bindinggal húzza be a postokat. 🙂 Mivel elég gyakori, hogy a publikálás után veszem észre, hogy összekevertem a télapó és a gestapo szavakat, hiába javítom, a devportalon nem változik. Javaslom, hogy aki neadjbéla arra vetemedik, hogy szerény kis agymenéseimmel múlatja az időt, kattanjon a postok alján lévő wordpress linkre, ott lesz a friss változat. Danke sün!

A generikus metódusokat/típusokat szeretjük, mert nagy flexibilitást, jobb teljesítményt és nagyobb típusbiztonságot adnak. Azonban semmi sincs ingyen – van, ahol nagyon meg tudja keseríteni az életünket az átállás generikus metódusokra. Vegyük a következő példát…

Jussunk el a problémáig!

Adott egy Arena nevű osztály, mely rendelkezik egy Harc nevű metódussal, amely két típusparaméterrel rendelkezik, továbbá megköti, hogy ezen T1 és T2 típusoknak kell, hogy legyen paramétermentes konstruktoruk, és meg kell valósítaniuk az IHarcképes interfészt. C#-ul mondva:

class Arena
{
    public string Harc<T1, T2>()
        where T1 : IHarcképes, new()
        where T2 : IHarcképes, new()
    {
        T1 t1 = new T1();
        T2 t2 = new T2();

        int delta = t1.GetHarcérték() - t2.GetHarcérték();
        if (delta > 0) return typeof(T1).Name + " az erősebb.";
        if (delta < 0) return typeof(T2).Name + " az erősebb.";
        return "Döntetlen.";
    }
}

interface IHarcképes
{
    int GetHarcérték();
}

Ahogy látható, a metódus létrehoz két objektumot, és az interfészmetódus segítségével megvizsgálja, melyik nyerne egy harcban. Tegyük fel, hogy sok osztályunk van, amelyeknek harcolniuk kell majd. Emellett szeretnénk ezeket párokba rendezni, mégpedig olyan módon, hogy a párokba rendezés dinamikusan történhessen, tehát ne legyen előre kőbe vésve. Néhány osztály, amivel el tudunk indulni:

class Katona : IHarcképes
{
    public int GetHarcérték() { return 1; }
}

class Ellenség : IHarcképes
{
    public int GetHarcérték() { return 2; }
}

class Lábas : IHarcképes
{
    public int GetHarcérték() { return 1; }
}

class Ellenlábas : IHarcképes
{
    public int GetHarcérték() { return 2; }
}

Érezhető, hogy a katona ellensége az ellenség lesz, illetve beigazolódik az is, hogy a konyhában minden lábasnak van egy ellenlábasa. (Maksa ftw!) Normál esetben a harcértéket valamiből kiszámolnánk; ehelyett itt sony-típusú randomszámokat adunk vissza. A párokba rendezést bízzuk egy osztályra:

class HarcpárTároló
{
    Dictionary<Type, Type> pairs = new Dictionary<Type, Type>();

    public HarcpárTároló()
    {
        pairs.Add(typeof(Katona), typeof(Ellenség));
        pairs.Add(typeof(Lábas), typeof(Ellenlábas));
    }

    public Type GetPairTypeFromType(Type t)
    {
        return pairs[t];
    }
}

Ebből gyárthatunk több leszármazottat is, és mindegyik a saját kis logikája alapján pároztathatja párosíthatja a típusokat. Eddig szép és jó. De hogyan hívjuk meg a Harc metódust? Próbáljuk meg valahogy…

Arena arena = new Arena();
HarcpárTároló hpt = new HarcpárTároló();

object egyik = new Lábas();
Type masik = hpt.GetPairTypeFromType(egyik.GetType());

arena.Harc<egyik.GetType(), masik>();  //compile-time error

Ez ugye fájdalmasan csúnya. Olyannyira, hogy le se fordul. (Sőt, ha valaki megpróbálta begépelni, érezhette, hogy az IntelliSense mindent megtesz, hogy akadályozza.) A probléma ugyebár az, hogy a generikus metódus meghívásához fordítási időben rendelkezésre kellene állnia a típusparamétereknek. Mit lehet tenni?

Tükrözés!

Talán ismerős lehet, hogy ismeretlen típusoknál a reflection segítségével elérhetünk ismeretlen metódusokat. De természetesen arra is képes a rendszer, hogy ismert típusok ismert metódusait adja vissza. 🙂

A metódus kiválasztása után csak plusz egy lépést kell elvégezni ahhoz, hogy működjön a dolog. Mivel itt generikus metódusról van szó, a metódus kiválasztása csupán a “nyílt” metódust adja vissza. Ez a generikus sablon, amit tulajdonképpen megírtunk C#-ban. Ahhoz, hogy ezt két konkrét típussal használhassuk, rá kell venni a fordítót, hogy készítse el a metódus nem-generikus változatát. (Ha a nyílt metódust hívnánk, egy InvalidOperationExceptiont kapnánk, ami arról tájékoztatna, hogy ez egy nyílt generikus metódust, és mint ilyen, meghívhatatlan. :)) Végül pedig ugyanúgy hívhatjuk a metódust, mintha bármilyen nem-generikus metódust hívnánk reflectionön keresztül: a zárt metódus már nem generikus.

MethodInfo openMeth = arena.GetType().GetMethod("Harc");
MethodInfo closedMeth = openMeth.MakeGenericMethod(
    new[] { egyik.GetType(), masik });
closedMeth.Invoke(arena, null);

Nem olyan szép, mintha meglennének előre a típusok, de ez így képes feldolgozni, ha csak futásidőben kapjuk meg őket. Készen vagyunk.

Lehetne ezt máshogy?

Természetesen: senki nem kötelez minket arra, hogy generikus metódust használjunk ott, ahol valami miatt kényelmetlen. A Harc metódust megírhatjuk típusmentesre is. Picit bonyolultabb lesz.

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

English (auto-detected) » English
public string HarcNonGeneric(Type t_t1, Type t_t2)
{
    if (!t_t1.GetInterfaces().Contains(typeof(IHarcképes)) ||
        !t_t2.GetInterfaces().Contains(typeof(IHarcképes)))
    {
        throw new InvalidOperationException(
            "Mindkét paramétertípus meg kell valósítsa az IHarcképes interfészt.");
    }

    IHarcképes t1 = (IHarcképes)Activator.CreateInstance(t_t1);
    IHarcképes t2 = (IHarcképes)Activator.CreateInstance(t_t2);

    int delta = t1.GetHarcérték() - t2.GetHarcérték();
    if (delta > 0) return t_t1.Name + " az erősebb.";
    if (delta < 0) return t_t2.Name + " az erősebb.";
    return "Döntetlen.";
}

Jobb ez a metódus? Röviden: nem, a generikus mindig szebb. 🙂 De ha objektívebben nézzük: le kell mérni, melyik mit tud.

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

English (auto-detected) » English

Egymillió hívásnál…

Lefuttattam a generikus és a nemgenerikus változat meghívását 1 milliószor, és a kiváncsiság kedvéért a nemgenerikusat reflectionön keresztül is. Ha csak a metódushívásokat nézzük, előre látható, hogy a generikus változat szanaszét veri a másik kettőt. (ms, az alacsonyabb érték a jobb.)

Generic, reflection: 12091
NonGeneric, reflection: 20399
NonGeneric, nonreflection: 16904

Ha mindig, vagy legalábbis sokszor kell ugyanazokkal a típusokkal dolgozunk, ugyanarra a típusra lefuttatnunk a metódust, akkor tehát megéri generikusan implementálni. A metódus elkészítésének overheadje csak egy egyszeri overhead. (A teljesség kedvéért megjegyzem, hogy a gép egyéb dolgaitól függően volt egy 1-3 mp-es kilengés a futtatások között, de az arányok mindig így alakultak.)

Mi van azonban, ha egy típusra (típuspárra) csak egyszer futtatjuk le, és megyünk is tovább a következő típusra/típuspárra?

Generic, reflection: 25157
NonGeneric, reflection: 22699
NonGeneric, nonreflection: 16916

Mivel minden egyes metódushívás előtt le kell kérni a metódust, és legyártatni a zárt változatát, megfordult a dolog: a nem-generikus metódus itt minden hátránya ellenére is sokkal gyorsabb, mint a generikus. Tehát ilyen esetekre jobban járunk, ha nem generikusat készítünk, hanem maradunk a “jól bevált” C# 1.0-s módszernél. Persze most Activatorral dolgoztunk – ha ennek a tudása nem elég. és ConstructorInfokon keresztül kell dolgozni, az okozhat némi lassulást a nemgenerikus változatban.

Mára ennyi, most pedig vissza kódolni.

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
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
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English

ObjectDumper – fordítsuk ki Caerbannog gyilkos nyulát!

Akkor így a nyúl-, tojás- és sonkaünnep (szigorúan nem nyúltojás- és nyúlsonka!) vasárnapján megosztok egy pici, hasznosnak gondolt kis osztályt.

Az ObjectDumpernek elkeresztelt kis valami arra lesz képes, hogy egy objektum belső állapotát – azt is, ami tényleg belső, privát – egy kellő mértékben formázott stringként adja a külvilág tudtára. Elsősorban hibakeresésre szánjuk az osztályt: nem kell a locals ablakban turkálni, csak hívni egy Dump()-ot egy objektumon, és kész is. A munka során megismerkedünk néhány reflectionnel kapcsolatos dologgal.

Kezdjük szokás szerint egy testreszabott osztállyal, ami az állatorvosi ló szerepét tölti majd be. A jeles alkalomra való tekintettel a ló most legyen nyúl.

A nyúl

Mindössze egy pici osztályra lesz szükség, ami itt-ott “furán néz ki”. Hogy megfelelően tesztelhető legyen az ObjectDumper, olyan osztályra lesz szükség, ami pl. rendelkezik olyan tulajdonsággal, ami csak lekérdezhető, vagy épp csak beállítható. Rakjuk össze a nyulat!

public class Rabbit
{
    public string Name { get; private set; }
    public DateTime BirthDate { get; set; }
    private int killedKnights;
    public int KilledKnights
    {
        set { killedKnights = value; }
    }
    private string favColor;
    private string FavouriteColor
    {
        get { return favColor; }
    }
    public Rabbit(string name, DateTime bDate
        int killedKnights, string favColor)
    {
        this.Name = name;
        this.BirthDate = bDate;
        this.killedKnights = killedKnights;
        this.favColor = favColor;
    }
}

Látható, hogy a nyuszinak lesz egy neve, egy születési dátuma, illetve beállítható lesz rajta, hogy eddig hány lovagot ölt meg. Viszont ez kívülről nem kérdezhető le, így egy lovag, amikor először meglátja, nem fogja tudni, mivel áll szemben. Ezen felül pedig lesz egy kedvenc színe is a nyúlnak, azonban ez nem fontos, hacsak nem akar hidakon átkelni.

A nyúlon túl

Mielőtt még nekilátnánk az ObjectDumpernek, szükségünk lesz néhány enumerációra, amivel majd szabályozhatjuk a működést. Elsőként: mezőket vagy tulajdonságokat jelenítsünk meg? Aztán: publikus vagy nem publikus tagokkal dolgozzunk? Végül: milyen plusz információt jelenítsünk meg? (Például a tag típusa, illetve az, hogy egy tulajdonság milyen accessorökkel rendelkezik.)

[Flags]
public enum DumpTarget : byte
{
    Fields = 0x01,
    Properties = 0x02
}
[Flags]
public enum DumpVisibility : byte
{
    Private = 0x01,
    Public = 0x02
}
[Flags]
public enum DumpAdditionalInfo : byte
{
    None = 0x00,
    Type = 0x01,
    PropAccessors = 0x02
}

Egy kis magyarázat: A FlagsAttribute jelzi, hogy itt egy bitflagről van szó, vagyis össze akarjuk majd kapcsolni az egyes értékeit az adott enumerációnak. (Tehát pl egyszerre akarunk publikus és privát tagokat megjeleníteni.) Ez most még annyira nem lényeges, de ha később szeretnénk kiterjeszteni az enumerációt plusz tagokkal, jól jöhet.

A byte, mint “ősosztály” itt azt jelenti, hogy az enumeráció mögötti tároló egy byte legyen. Max. 256 értéket vehet tehát fel az enum, cserébe negyedannyi helyen elfér, mint az alapértelmezett integer.

Az enumok tagjai után lévő hexaszámok csak a címke mögötti konkrét értéket jelzik.

Mehetünk tovább – kezdjük el fejleszteni a lényeget. 🙂

Minden objektum hozzon magával még egy metódust!

Alapvetően nem a legjobb dolog, ha a System.Objectre rakunk bővítőfüggvényt (hogy miért, arról majd később), de megtehetjük, és nekünk most logikailag ez a legmegfelelőbb. A bővítőfüggvények (extension methods) a C# 3.0-ban jelentek meg. Segítségükkel bármilyen osztályra felrakhatunk  plusz metódusokat – látszólag. Valójában az ilyen módon az osztályra rakott metódus továbbra sem lesz ténylegesen az osztály része; pusztán a C# engedi nekünk, hogy szintaktikailag egy példányon meghívjuk, de a háttérben a hívás nem a példányon történik, hanem a bővítőfüggvényt tartalmazó osztályon.

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English

Elég a szóból, hozzuk létre az osztályt, benne a függvénnyel. Rögtön adjuk meg a paramétereket is: az első nyilvánvalóan az objektum, aminek a tartalmát dumpolni akarjuk, utána következzen egy TextWriter, ami a kimenetet tárolja majd, majd jöhet a 3 fentebb készített enumeráció argumentumként való fogadása. A paraméterlista összeállításakor izomból felhasználjuk a C# 4.0 opcionális/nevesített paraméterezési képességeit, vagyis ahol lehet, rögtön default értéket adunk a paramétereknek.

public static class ObjectDumper
{
    public static void Dump(this object o,
        TextWriter output = null,
        DumpTarget targets = DumpTarget.Fields | DumpTarget.Properties,
        DumpVisibility visibility = DumpVisibility.Public,
        DumpAdditionalInfo additionalInfo = DumpAdditionalInfo.None)
        {
        }
}
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English

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

English (auto-detected) » English

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (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

Tekintve, hogy bővítőfüggvényről van szó, egy public static osztályban public static módosítókkal kell létrehozni a metódust. A kódot picit később írjuk meg.

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

English (auto-detected) » English

Hogy a formázást szeretnénk egyszerűen megoldani, érdemes lesz ezt is az osztály szintjén definiálni, így egy központi helyen lesz állítható. Csupán néhány stringet kell létrehozni:

public static string FieldsHeader = "---Fields";
public static string PropertiesHeader = "---Properties";
public static string PublicHeader = "\t---public";
public static string PrivateHeader = "\t---nonpublic";
public static string IndentChars = "\t";
public static string EqualitySign = " = ";

Most, hogy megvagyunk a körítéssel, írjuk meg a Dump metódus logikáját!

Először is ellenőrizzük, hogy nem null-referencián hívták-e a metódust. Ha igen, a metódus dobjon egy ArgumentNullExceptiont. Mindenki döntse el maga, hogy ez a legmegfelelőbb kivétel-e ebben a szituációban. Mivel bővítőfüggvényről van szó, amelynek első paramétere okozta a hibát, hajlok arra, hogy inkább ezt javasoljam, mint egy NullReferenceExceptiont.

Ha nem nullt kaptunk, kérdezzük le a kapott objektum típusát. A típusinformációk segítségével fogjuk felderíteni az objektum belsejét.

Ezután jöhet a StringBuilder létrehozása. Mivel nem akarjuk, hogy fél mega szemét maradjon a memóriában a metódus lefutása után, jobb lesz, ha nem stringeket konkatenálunk, hanem egy StringBuilderrel rakjuk össze a kimenetet. Ebbe az objektumba rögtön írjuk is be, hogy milyen típusú objektumot “dumpolunk” éppen.

if (o == null) throw new ArgumentNullException(
    "o", "Dump cannot be called on a null reference.");
Type t = o.GetType();
StringBuilder sb = new StringBuilder("Dumping ");
sb.AppendLine(t.Name);

Ezek után jöhet a mezők és a tulajdonságok felsorolása attól függően, hogy a hívó mit kért. Ezt ugye a targets paraméter alapján tudjuk eldönteni.

Tekintve, hogy lehetséges, hogy több értéket is megjelölt a hívó, nem tudunk egyszerű értékösszehasonlítást végezni, helyette a HasFlag metódust kell meghívni. Ugyanezt kell elvégeznünk a visibility paraméter vizsgálatakor is.

Ha megvolt a vizsgálat, indulhat a foreachelés egy megfelelően lekérdezett FieldInfo tömbön. A megfelelően lekérdezett itt azt jelenti, hogy a típusobjektumon meghívott GetFields metódusnak a BindingFlags enumeráción keresztül megmondjuk, hogy pontosan milyen láthatóságú tagokat kérünk. A munka oroszlánrészét, vagyis a kiíratást egy privát metódus oldja majd meg, melyet a következő lépésben definiálunk.

Mivel a tulajdonságok lekérdezésének ugyanez a logikája, csupán az információs osztályok típusa, illetve a belső logika változik, erre nem térek ki külön.

if (targets.HasFlag(DumpTarget.Fields))
{
    sb.AppendLine(FieldsHeader);
    if (visibility.HasFlag(DumpVisibility.Private))
    {
        sb.AppendLine(PrivateHeader);
        foreach (FieldInfo fi in t.GetFields(
            BindingFlags.NonPublic | BindingFlags.Instance))
        {
            getFieldString(sb, o, fi, additionalInfo);
        }
    }
    if (visibility.HasFlag(DumpVisibility.Public))
    {
        sb.AppendLine(PublicHeader);
        foreach (FieldInfo fi in t.GetFields(
            BindingFlags.Public | BindingFlags.Instance))
        {
            getFieldString(sb, o, fi, additionalInfo);
        }
    }
}

Kész is. A fenti elágazást – a megfelelő helyeken józan paraszti ész alapján módosítva – lemásoljuk, és ezzel lekérdezzük a tulajdonságokat is.

Már csak annyi van hátra a sok útelágazódás után, hogy a metódus végére telepakolt StringBuilder tartalmát a kapott TextWriter objektumba pakoljuk. Tekintve, hogy az output default értéke null, ezt le kell ellenőriznünk, mert valószínű, hogy lesz, aki így használja. Ha nullt kaptunk, kimenetnek megadjuk a konzolt. Ezek után pedig kiírjuk az sb változó tartalmát.

if (output == null) output = Console.Out;
output.WriteLine(sb.ToString());

A tartalom lekérdezése

A következő lépés a getFieldString metódus, illetve párja, a getPropertyString metódus megírása lesz. (A név egy kicsit megtévesztő lehet: nem stringgel térnek vissza, hanem az eredeti StringBuildert töltik fel további adatokkal.)

private static void getFieldString(StringBuilder output,
    object dumpedObj, FieldInfo fInfo, DumpAdditionalInfo adtnl)
{
    output.Append(IndentChars);
    if (adtnl.HasFlag(DumpAdditionalInfo.Type))
        output.AppendFormat("[{0}]\t", fInfo.FieldType);
    output.AppendFormat("{0}{1}{2}{3}", fInfo.Name, EqualitySign,
        fInfo.GetValue(dumpedObj) ?? "{null}", Environment.NewLine);
}

Mit csinálunk itt?

Először is behúzzuk a sört. Csak, hogy jobb legyen a kedv. (Ja, húsvét van?) Aztán a sort, hogy olvashatóbb legyen a kimenet.

Ezután, ha a a hívó kérte, a FieldInfo FieldType tulajdonságának segítségével kiírjuk a mező típusát. Ez opcionális.

Utolsó körben pedig jöhet lényeg: szépen formázva kiírjuk a mező nevét, majd tartalmát, majd törjük a sort. Közben annyira kell még ügyelnünk, hogy ha esetleg a mező tartalma null lenne, írjunk ki helyette egy normál stringet. (null coalescing op ftw!)

Nagyon hasonló, amit a tulajdonságokkal el kell játszani, de van néhány lényeges különbség.

A tulajdonságok meglehetősen összetettek. Gyakorlatilag 1 vagy 2 metódusunk van – elképzelhető olyan szituáció is, amikor csak set van, vagyis a tulajdonság kívülről nem kérdezhető le, csak beállítható. Ráadásul ezek a metódusok nem feltétlenül publikusak; saját láthatóságuk van. Ezen felül akár még kivételt is dobhatnak…

Lássuk, mit tehetünk!

private static void getPropertyString(StringBuilder output,
    object dumpedObj, PropertyInfo pInfo, DumpAdditionalInfo adtnl)
{
    output.Append(IndentChars);
    if (adtnl.HasFlag(DumpAdditionalInfo.Type))
        output.AppendFormat("[{0}]\t", pInfo.PropertyType);
    output.AppendFormat("{0}{1}{2}", pInfo.Name, EqualitySign,
        pInfo.GetGetMethod() != null ? pInfo.GetValue(dumpedObj, null)
        ?? "{null}" : "{no public get accessor");
    if (adtnl.HasFlag(DumpAdditionalInfo.PropAccessors))
    {
        output.AppendFormat(" [{0}{1}]",
            pInfo.GetGetMethod(true) != null ? "get;" : string.Empty,
            pInfo.GetSetMethod(true) != null ? "set;" : string.Empty);
    }
    output.AppendLine();
}

Csak egy kicsit bonyolultabb, na. 🙂 Az elejére ne vesztegessünk szót, nem sok változás van a korábbiakhoz képest.

Az érték kiírásánál már figyelnünk kell arra, hogy egyáltalán lekérdezhetjük-e az értéket. Ha nincs get accessor, meg sem próbáljuk.

Ezek után, ha a hívó kérte, kiírjuk, hogy milyen accessorökkel rendelkezik a tulajdonság. A PropertyInfo GetGetMethod és GetSetMethod metódusának lekérdezése éppen megfelelő lesz erre.

Készen vagyunk, teszteljük le, mit alkottunk!

“Egy nyúlpörkölt rendel.”

Készítsünk egy nyulat, és hívjuk meg rajta a metódust!

Rabbit bugs = new Rabbit("Killer Rabbit of Caerbannog",
    new DateTime(1975, 4, 3), 90, null);
bugs.Dump(visibility: DumpVisibility.Private | DumpVisibility.Public,
additionalInfo: DumpAdditionalInfo.Type |
DumpAdditionalInfo.PropAccessors);

Futtassuk meg, és nézzük meg, mit rejtett a nyúl belseje!

Dumping Rabbit
---Fields
        ---nonpublic
        [System.Int32]  killedKnights = 90
        [System.String] favColor = {null}
        [System.String] k__BackingField = Killer Rabbit of Caerbannog
        [System.DateTime]       k__BackingField = 1975.04.03. 0:00:00
        ---public
---Properties
        ---nonpublic
        [System.String] FavouriteColor = {no public get accessor} [get;]
        ---public
        [System.String] Name = Killer Rabbit of Caerbannog [get;set;]
        [System.DateTime]       BirthDate = 1975.04.03. 0:00:00 [get;set;]
        [System.Int32]  KilledKnights = {no public get accessor} [set;]

Egész jó. Vajon hol vannak az easter eggek (=bugok)? 🙂 (Aki talál, kap csokitojást. Viccen kívül.)

Update: elfelejtettem letölthetővé tenni a kész kódot. Íme. 🙂

http://cid-0b0abf2a681faf43.office.live.com/embedicon.aspx/.Public/chevenix.wordpress.com/ObjectDumper.zip

További lehetőségek

Az osztály korántsem 100%-os, de arra bőven jó, hogy elinduljunk. Én olyan 85%-osnak mondanám, tekintve, hogy a formázás testreszabása sokkal bonyolultabb is lehetne. 🙂

Mit lehetne még beépíteni? Például a statikus tagokat abszolúte kihagytuk – elvileg ez nem gond, mivel mi egy konkrét példányt dumpolunk, de ha érdekel valakit, esetleg ezt is bele lehet rakni. Aztán a tulajdonságoknál lehetne még bonyolítani a dolgot jócskán. Valamint például beépíthetnénk elvárt/kizárt értékek megadását. Vagy a tagok attribútumait is beolvashatnánk. Jelezhetnénk, hogy az adott tulajdonságot ezen a típuson definiálták, vagy örökölte.

A végtelenségig lehetne tuningolni ezt a metódust és osztályt. Viszont még van pár másik program, amire rá kell néznem, és amelyekért talán pénz is jön, úgyhogy tartva magam ahhoz, ami lassan itt mottóvá válik…

…most vissza kódolni. 🙂

Hogyan működik a Dictionary?

A minap feltettek nekem néhány kérdést a .NET működésével/használatával kapcsolatban. A legtöbbre rögtön tudtam a választ (legfeljebb egy kicsit nehéz volt angolul, ábrák felrajzolása nélkül kifejteni, hogy mik is az érték- és referencia-szerinti paraméterátadás nüansznyi, de ordas nagy hibalehetőségeket hordozó különbségei érték- és referenciatípusok esetén 🙂 ), de egy kérdés kicsit leizzasztott. Körülbelül így szólt: átlagosan mennyi idő – hány művelet elvégzése – alatt tárol el egy adatot egy hashtábla?

Mindezt ugye Ordoban (Ο – én most az omikront használom helyette, amíg meg nem találom a kódját) megadva.

A fenti szimbólumot iskolában/munkában kb. 6-7 éve láttam utoljára, úgyhogy a testhőmérsékletem pillanatok alatt a 310 Kelvint kezdte súrolni, és amennyire éreztem, a pulzusom is 3 digitesre nőtt. 🙂 De elővettem a .NET-es tudásomat a Dictionaryről – amit úgy háromhavonta le is kell oktatnom -, és nekiálltam levezetni, hogy mennyi is lehet az annyi.

Hogyan működik egy Dictionary?

(Tekintve, hogy a Hashtable egy típusbizonytalan osztály, inkább a .NET 2.0-ban megjelent utódján, a Dictionary<K,V>-n magyarázok.)

A Dictionary kulcs-érték párokat tárol. Amikor hozzáadunk egy új párt, a kulcs alapján valahogy elhelyezi az adatot a memóriában. Amikor kíváncsiak vagyunk egy értékre, akkor a kulcs alapján kereshetünk rá; vagyis megadunk egy kulcsot, és ha az szerepel a szótárban, akkor valahogy előkeresi, és visszaadja a hozzárendelt értéket. Ha nem szerepel, akkor kapunk egy KeyNotFoundExceptiont. Ha egy olyan kulcsot próbálunk hozzáadni a szótárhoz, ami már szerepel benne, akkor a szótár ezt valahogy megérzi. 🙂 Dob egy kivételt, mert egy kulcs csak egyszer szerepelhet. Eddig valószínűleg mindenkinek ismerős.

A lényeg, hogy ez a sok valahogy mit is takar pontosan. Hogyan helyezi el az adatot a memóriában? Hogyan keresi elő? Mi alapján mondja meg, hogy két kulcs megegyezik?

Kezdetnek álljon itt egy példa és némi kód, hogy legyen min prezentálni.

2390-ben a UFP épp elnököt választana. A különböző bolygók jelöltjeit egy szótárban gyűjtik össze. (Életszerű szituáció.)

Legyen tehát egybolygó osztályunk; tartalmazza a bolygó nevét és lakosságát.

public class Planet
{
    public string Name { get; set; }
    public int Population { get; set; } //billion people
}

Hozzunk létre egy szótárat, és adjunk hozzá néhány bolygót. A szótárban a kulcs a bolygó lesz, a hozzá rendelt érték pedig a jelölt neve. Tegyük fel, hogy az adatok egyenként érkeznek be, a rendszer nem valós bolygó objektumokra vár referenciát, hanem a jelölt maga adhatja meg az őt jelölő bolygó adatait.


Dictionary<Planet, string> candidates = new Dictionary<Planet, string>();

Console.WriteLine("Adding Picard of Earth...");
candidates.Add(new Planet
    { Name = "Earth", Population = 9 }, "Jean-Luc Picard");
Console.WriteLine("Adding Brex of Bolarus IX...");
candidates.Add(new Planet
    { Name = "Bolarus IX", Population = 8 }, "Solian Jarso Brex");
Console.WriteLine("Adding Dax of Trill...");
candidates.Add(new Planet
    { Name = "Trill", Population = 9 }, "Ezri Dax");
Console.WriteLine("Adding Khan of Earth...");
candidates.Add(new Planet
    { Name = "Earth", Population = 9 }, "Khan Noonien Singh");

No, ezzel megvolnánk. A probléma csak annyi, hogy ez így le is fut, márpedig Khan csalni akar: ugyanazt a bolygót mondja hazájának, mint Picard. Mivel két különböző objektumról van szó (hiszen mindkét jelölt saját maga készített egy Planet példányt az Add metódus meghvásakor), a Dictionary különböző objektumnak látja őket, tehát engedni fogja, hogy a Földnek két jelöltje legyen. Ezt a galádságot csakis a Flotta .NET-programozói előzhetik meg! 🙂

Hogyan vizsgálja a Dictionary a kulcsegyezőséget?

A Dictionary az egyes kulcsok két metódusát használja a kulcsegyezőség felderítésére: A GetHashCode-ot, illetve az Equalst. Mindkettő a System.Objecten definiált, tehát az égegyadtavilágon minden objektum megörökli.

Amikor egy kulcsnak megpróbál helyet találni, a szótár meghívja a kulcs GetHashCode metódusát. Ez a metódus egy integert ad vissza – ha nem override-oltuk, akkor a keretrendszer egy – az AppDomainben legalábbis – egyedi számot ad vissza, tehát minden objektum más és más hashsel fog rendelkezni. Ha felülírjuk, akkor nekünk kell valamilyen úton-módon egy hash-értéket kiszámolni az objektumunkból. (Eme érték kiszámolásának szabályairól, buktatóiról majd egy későbbi postban írok.)

Ez eddig szép, de mire jó? Eltárolja egy listában a hasht, és mellette az értéket? Korántsem.

Hogyan tárolja el a Dictionary az elemeket?

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

A kapott hashből a Dictionary el tudja helyezni az elemet a magában tárolt tömb egyik helyére. Vagyis egy objektum hashkódja és a tárolási memóriacím között tulajdonképpen egyértelmű leképezés van…. elvileg. Tökéletes hash-algoritmus használata esetén. Egyelőre ezt még a Borg is keresi.

Tehát: mind az elhelyezés, mind a későbbi visszakeresés átlagosan Ο(1) lépés alatt végbemegy – csak le kell kérdezni, és át kell transzformálni a hasht egy tömbindexre. Valójában a lentebb részletezett okok miatt ez nem 1, hanem mondjuk 1+x, de mindenképpen kicsi és konstans.

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

English (auto-detected) » English

Ellenpéldának ott van mondjuk egy elem beszúrása egy tömbbe, aztán előkeresése: beszúrni Ο(n), hiszen legrosszabb esetben – vagyis ha a tömb első helyére akarunk beszúrni -, n db lépésbe kerül shiftelni a tömböt. Keresésnél szintén O(n) lépés lehet a dolog, hiszen végig kell mászni a tömbön, hogy megtaláljuk a keresett elemet. Egy szótárnál az elem helyét azonnal megadja a hashcode átalakítása.

Kössünk bele a vizsgálatba!

Ennyi magyarázat után akkor most lássunk munkához! Írjuk felül a GetHashCode-ot a Planet osztályban.

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

English (auto-detected) » English
public override int GetHashCode()
{
    Console.WriteLine("\tGetHashCode called on " + Name);
    return Name.Length * Population;
}
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddishAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish

English (auto-detected) » English

Az első sor természetesen nem szükséges, csak azért szúrtuk be, hogy amíg teszteljük a működést, lássuk, mikor hívódik a metódus. A második sor a lényeg: fogjuk a bolygó nevének hosszát, és összeszorozzuk a lakossággal. Ez elvileg már ad némi differenciát az egyes bolygók hashe között – ugyanis az a jó algoritmus, ami az értékkészleten jól szétoszlik.

(Megj.: simán az is elég lett volna, ha a Name.GetHashCode()-ot adjuk vissza, de egyszerűen nincs szívem ennyire suta “algoritmust” gyártani. 🙂 )

Próbáljuk ki… az eredmény talán meglepő: még mindig lefut a program.

Az egyezőségvizsgálat második fele

Ez azért történik, mert ha a Dictionary két azonos hasht észlel (vagyis észreveszi, hogy az adott cím már foglalt a tömbben), akkor meghívja az eltárolt elem kulcsának Equals metódusát, és átadja ennek az új kulcsot. A feladat tehát adott, írjuk felül az Equalst is!

public override bool Equals(object obj)
{
    Console.WriteLine("\tEquals called on " + Name);
    Planet other = obj as Planet;
    if (other != null && Name == other.Name)
        return true;
    return false;
}

Innentől megy is minden. Vagyis épp ellenkezőleg: kivételt kapunk, amikor Khan megpróbál csalni, de pont ez volt a célunk.

Lássuk, mit főztünk!

Érdemes belenéznünk a futás eredményébe:

 Adding Picard of Earth...
     GetHashCode called on Earth
 Adding Brex of Bolarus IX...
     GetHashCode called on Bolarus IX
 Adding Dax of Trill...
     GetHashCode called on Trill
     Equals called on Earth
 Adding Khan of Earth...
     GetHashCode called on Earth
     Equals called on Trill
     Equals called on Earth

…ééés ArgumentException.

Ebből látszik, hogy minden egyes elem hozzáadásakor hívódik a GetHashCode: a Dictionary meghatározza a tárolás helyét.

Ami érdekes, hogy az Equals nem csak Khannál hívódik, hanem már Daxnál is! Nem véletlenül: a “Trill”.Length * 9 éppen annyi, mint az “Earth”.Length * 9, tehát az algoritmusunk ugyanazt a hasht számolta ki mindkét elemre. Ekkor a szótár meghívja az Equalst a már meglévő kulcson (Picard Earth objektuma), amely közli, hogy nincs gond, az új kulcs nem azonos a régivel.

Node akkor van egy kis gond. Két kulcs adta ugyanazt a hasht, de az Equals szerint ezek a kulcsok különbözőek, tehát szerepelhetnek egyszerre a szótárban.   Ugyanabba a “cellába” kellene rakni két elemet. Ilyenkor mi történik?

Két azonos hash-ű, de különböző kulcs esete

A Dictionary éppen amiatt, mert nem lehet tökéletes hash-algoritmust találni minden típusra, logikailag nem csak hash-elem párokban gondolkodik, hanem minden hash-hez kezelni tud egy “vödröt”. Ha két azonos hash-ű, de különböző kulcsot kell tárolnia, akkor kénytelen ugyanabba a “vödörbe” helyezni az új elemet, mint amiben a régi van.

Vagyis innentől, ha egy olyan elemet próbálunk kivenni, amelynek kulcsa a fenti két bolygó egyike, akkor több lépés lesz megtalálni az elemet, mert nem elég a hash alapján kiválasztani a helyet: még végig kell menni a vödörben lévő elemeken, amíg meg nem találjuk a keresettet. Tehát egy rossz hashelési algoritmus leronthatja a Dictionary teljesítményét – de nagy elemszámnál még így is jóval gyorsabb lesz egy hashtábla, mint egy tömb.

A Trill jelölt hozzáadása utáni lépésnél már világos, hogy mi történik. A Dictionary megkapja a hasht Khan bolygójától. Észreveszi, hogy az a hash már foglalt: összehasonlítja az összes egyező hashű elemmel ezt az új kulcsot. Meghívja az Equalst a korábbi Trill, illetve Earth objektumon. Utóbbi true-t ad vissza – ekkor a Dictionary dob egy kivételt, mert a kulcs már szerepel.

Hogyan történik a keresés?

Csak, hogy megbizonyosodjunk az eddigiek helyességéről, írjunk be néhány sor plusz kódot a Main metódusunkba, a korábbiak mögé. (Természetesen kommentezzük/töröljük a Khanos sort, ami kivételt dob!)


Console.WriteLine("---Reading the name of the candidate from Trill---");
Console.WriteLine(candidates[new Planet
    { Name = "Trill", Population = 9 }]);

Console.WriteLine("---Reading the name of the candidate from Earth---");
Console.WriteLine(candidates[new Planet
    { Name = "Earth", Population = 9 }]);

Világos, mit szeretnénk: előhúzni két elemet a kalapból, méghozzá pont azokat, akikről tudjuk, hogy ugyanaz a hashük. Az eredmény az lesz, amit várunk: kapunk egy hash-vizsgálatot (a kulcson), majd amikor ebből meghatározta az elem tömbbéli indexét, jön a vödör elemein való végiglépkedés, Equals-hívás, amíg meg nem találja az azonos kulcsú elemet.

Összegezve, röviden

A hashtáblák, így a Dictionary<K, V> működésének alapja, hogy a beléjük helyezett elempárok kulcs részét egy függvény segítségével egy integerré alakítják, amely integer megadja, hogy a hashtábla mögötti tömb mely helyére kell beilleszteni az adott kulcs-érték párt. (A .NET-ben ez a függvény rendszerszinten része minden objektumnak, de ahogy fentebb is láttuk, saját típusnál könnyen lecserélhető.) Ennek köszönhetően mind az elem hozzáadása, mind a megkeresése egyetlen lépésben megoldható.

Abban az esetben, ha elérjük a hash függvény korlátait, vagyis két különböző elem ugyanazt a hasht adja vissza, a hashtábla egy láncolt listát hoz létre az adott elemhez, mely listában eltárolja az összes adott hash-ű kulcs-érték párt. (Tudtommal a Dictionary a list heades chaininget implementálja, de akárhogy kutatok, erről nem találok infót. Ha nem így lenne, javítsatok ki!) A beszúrás/keresés ideje ilyenkor néhány lépéssel megnő, de a legrosszabb kivételével  (amikor minden elemnek ugyanaz a hash-e) minden esetben alacsonyabb lesz, mint egy tömbnél lenne.

További okosságok

Lenne még miről írni. Bőven. Fogok is, ha legyőztem a WorkItemek hadseregét. 🙂 Ott van például az, hogy mire kell ügyelni egy saját hash-algoritmus írása esetén. (Másképpen fogalmazva: miért ne használj soha olyan hash-algót, amit fentebb írtunk a Planet osztályhoz. :D) Vagy, hogy hogyan tudunk az elemek összehasonlításába beleszólni, ha nem saját típusúak az elemek, vagyis nem tudjuk felülcsapni a göthöskód és ikvolsz metódusokat. Vagy, hogy mi az a load factor, mit tudunk befolyásolni vele. Illetve, talán érdemes lesz megvizsgálni a Dictionary<K,V> konkrét megvalósítását is, metódusok szintjén.

Mára azonban legyen elég ennyi. Remélem, segített valakinek a fenti sok-sok ujjkoptatás. 🙂

Hihetetlen, de szombat éjjel is csak azzal tudom zárni a postot, hogy…

…és most vissza kódolni.

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
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
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
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

Fókusz áthelyezése egy WPF DataGridben

Nem rég szembejött a probléma, hogy egy sokablakos, bonyolult WPF-app működése során néha kézzel – vagyis kóddal – kellene a fókuszt egy DataGrid egy sorára helyezni. A DataGrid.Focus() és a SelectedIndex beállításának kombója nem járható út, ilyenkor maga a Grid kapja meg a fókuszt, nem lehet rögtön lépkedni benne a le/fel nyilakkal.

Mit tehet ilyenkor az ember? Kiválasztja az adott sort a Rows tulajdonságból, és meghívja azon a Focust. Igen ám, de nem. Nincs Row/Rows tulajdonság, amiből kiválaszthatnánk a sorokat, illetve nincs GetRow metódus. Itt kezdené visszasírni az ember a DataGridViewt. 🙂

(Amúgy nem. Épp valamelyik nap kellett vele dolgoznom, és így 2-3 év WPF-ezés után már érzi az ember, hogy a DataGrid barátibb. Bár ez ízlés dolga.)

Akkor mit lehet tenni?

Nos, a DataGridnek, mint minden ItemsControlnak van egy Items tulajdonsága. Ezen keresztül elérhetjük az elemeket, amelyeket meg kell jelenítenie a gridnek. A probéma csak az, hogy ezek tényleg a megjelenítendő elemek. Az adatok a grid mögött, nem pedig a vizuális fában megjelenő sorok/cellák. Magyarán ez egy objectekből álló lista, tehát esélyünk sincs Focust hívni rajtuk, és kasztolni is max. az adattípusra tudjuk, amivel megint nem vagyunk beljebb.

Nem húzom tovább a dolgot (nem lenne mókás, ha végigvinnélek titeket azon a fél órás hajhulláson, amíg eljutottam a megoldásig 🙂 ), inkább prezentálom a végeredményt.

public static class DataGridExtender
{
    public static bool SetFocusOnDataGridRow(this DataGrid dg,
        int rownum = 0, bool setSelectedIndex = true)
    {
        if (rownum >= 0 && rownum < dg.Items.Count)
        {
            DataGridRow dgrow = (DataGridRow)dg.ItemContainerGenerator
                .ContainerFromItem(dg.Items[rownum]);
            if (setSelectedIndex) dg.SelectedIndex = rownum;
            return dgrow.Focus();
        }
        return false;
    }
}

Na, nézzük, mit látunk…

Ahogy látható, a C# 3.0 extension method (bővítőfüggvény) rendszerét használjuk, hogy a DataGrid osztálynak adjunk egy plusz metódust. A rownum paraméterrel várjuk, hogy mely sort akarják kiválasztani, a setSelectedIndex pedig azért “kell”, mert a fókuszáthelyezés és a grid kiválasztott elemének beállítása két külön dolog – ez a metódus képes lesz külön kezelni ezeket. A paraméterlistában kihasználom a C# 4.0-ás optional/named parameter fejlesztését, így a metódus gyakorlatilag paraméterek átadása nélkül is hívható lesz.

Ezek után jön a varázslat. A DataGrid ItemContainerGeneratorjának pontosan az a feladata, hogy egy adott adatelemhez (amit a fentebb ismertetett módon ki tudunk szedni a gridből) adjon nekünk egy olyan objektumot, ami a vizuális fában reprezentálja majd ezt az elemet. Vagyis eme objektum segítségével tudjuk áthidalni a szakadékot a megjelenítendő adat és a konkrét vizuális reprezentáció között.

A következő sor gondolom, világos: ha a programozó kérte, a selectedindexet is beállítjuk a sorra.

Ezután jön az, hogy akkor ki is jelöljük a sort, vagyis rárakjuk a fókuszt. Látható, hogy ez sem nehéz.

Ha kipróbáljátok, látni fogjátok, hogy azért nem tökéletes a leányzó fekvése. A sort kijelöli, a fókuszt is rárakja, de nem lehet rögtön mozogni a nyilakkal, mert a DataGridben cellánként megy az ugrálás – és most még nincs kijelölt cella.

A WPF-nek van egy nagyon szép bonyolult fókuszkezelő alrendszere, ami hajlamos igen randomisztikusan működni, de itt speciel pont jó lesz nekünk. Ha lecseréljük a “fókuszátrakó” sort a következőre, máris kijelöli az első cellánkat a fókusz rárakásánál:


return dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

Ennyi. Máris azonnal lépkedhető a gridünk. Remélem, sikerül megspórolnom némi kollektív hajhullást. 🙂

A WorkItemek nem oldják meg magukat, úgyhogy most (megintcsak) vissza kódolni.

ui.: bocs a kódszöveg mérete miatt, még harcolok a wordpress-szel, hogy ne legyen satu.

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

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

Console.WriteLine(“Hello world!”); //újra

Újra itt. Vagyis ezúttal itt.

Mikor a Live lelőtte a blogszolgáltatását, odalett az évek óta építgetett blogom.

Mielőtt valaki megkérdezné: igen, tudom, hogy át lehetett volna migrálni ide a korábbi postokat. Egy hétig leveleztem anno a wordpress-szel, de nem tudták megoldani a hibát, amit a rendszerük dobott, amikor megpróbálkoztam a migrálással… Szóval hagytam a csudába.

Mivel azonban a saját blogmotor még nincs meg (@zoltanarvai, rémlik még? :D), és mondanivalóm meg lenne, beadom a derekam, és nyitok egy brendnyú blogot. Magyarul, mert hiába szedtem össze pontokat az MSDN-en, az itthon nem számít community tevékenységnek. 😉

Aktuálisan egy nagyobb (=szívatósabb) .NET4/WPF4/EF4 projekten dolgozom, ami naponta idéz elő tetemes mennyiségű hajhullást. Igazság szerint emiatt kb naponta lett volna írnivalóm, dehát a tényleges munka mellett keveseknek van ereje értelmesen blogolni is, úgyhogy eleddig inkább… aludtam. 🙂

Emellett Windows Phone 7-fejlesztést oktatok “Magyarország második legöregebb egyetemén” (itt most igazat írtam, de van benne egy csepp szarkazmus is… egy kb 3 hektoliteres csepp), illetve konstans tartok .NET-tanfolyamokat az iparban dolgozó fejlesztőknek, valamint főleg nyaranta egyetemi hallgatóknak a Microsoft Magyarország megbízásából.

Illetve szeretek fordítani. 🙂 Ha valaki neadjbéla magyar nyelven használná a Visual Studio 2010-et – pontosabban feltelepítette hozzá a CLIP nevű Ms-programot, ami lefordítja a felületet -, és talál néhány magyarnak ugyan magyar, ámde archaikus megfogalmazású/nyelvezetű elemet, akkor sikeresen belefutott egy easter eggbe, amit azért raktam bele, mert éppen egy vidám pillanatomban fordítottam. Aki talál egy ilyet, és beírja kommentbe, kap tőlem egy üveg sört/tábla csokit. 🙂

Aki ismer, tudja, hogy fárasztó/borzasztó a humorom, és időnként ez átszüremlik a blogpostjaimba is. Úgyhogy t. Olvasó, vedd úgy, hogy figyelmeztettelek!

…és akkor most ennyi “indítószöveg” után… irány vissza kódolni.

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDetect languageDutchEnglishEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekHaitian CreoleHebrewHindiHungarianIcelandicIndonesianIrishItalianJapaneseKoreanLatinLatvianLithuanianMacedonianMalayMalteseNorwegianPersianPolishPortugueseRomanianRussianSerbianSlovakSlovenianSpanishSwahiliSwedishThaiTurkishUkrainianUrduVietnameseWelshYiddish
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBulgarianCatalanChinese (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