2010. január 15., péntek

Visual Studio 2010 Sexy Edition

A közelgő Visual Studio 2010 releassel kapcsolatban egy érdekes pletyka szivárgott ki a napokban a Microsofttól. A szoftverfejlesztő keretrendszernek egy speciális kiadása is elérhetővé válik majd 2010 április 12.-én: a Visual Studio Sexy Edition. A programról a splash screen mellett néhány technikai információ is napvilágot látott.

A kiadás érdekessége, hogy a már megszokott programnyelveket - a C#-ot, illetve Visual Basic .NET-et - nem támogatja a kiadás, és az újdonságként érkező F# nyelven sem tudunk alkalmazásokat fejleszteni segítségével, mindössze egyetlen programnyelvet, az abszolút újdonságként debütáló L#-ot fogja támogatni.

A Comenius Logo fejlesztői közösség máris azzal vádolja a redmondiakat, hogy az L# tulajdonképpen egy nagy nyúlás, egy az egybe' a logóról, mindössze a bonyolultabb nyelvi elemeket hagyták el. Egyesek jogi lépéseket, mások öri harit fontolgatnak.
A LOLCODE fejlesztők viszont üdvözölték az újonnan megjelenő lehetőséget, mert bár .NET LOLCODE compiler eddig is létezett, de most végre ők is a .NET ecosystem first class citizenjei lehetnek - igaz, ehhez egy új nyelvet kell elsajátítaniuk. Szerencsére ebben segít a közeljövőben megjelenő Teach yourself L# in one hour (including a 10 minutes long coffee break) című könyv.

2010. január 8., péntek

Enum.TryParse

Tegyük fel, hogy van egy ilyen enumerációnk:

enum Fruit
{
    Apple,
    Pear,
    Grape
}

Amennyiben valahonnan egy ilyen enum értéket sztringként kaptunk meg (tehát pl. volt egy olyan sztringünk, hogy "Apple"), és azt szerettük volna a konkrét enum értékke alakítani, az Enum.Parse() statikus metódushoz fordulhattunk segítségért:

public static void ParseEnum(string fruitName)
{
    Fruit fruit = default(Fruit);
    bool parsingSuccesful;

    try
    {
        fruit = (Fruit)Enum.Parse(typeof(Fruit), fruitName);
        parsingSuccesful = true;
    }
    catch (ArgumentException)
    {
        parsingSuccesful = false;
    }

    if (parsingSuccesful)
    {
        Console.WriteLine("Enum parsed succesfully, value: {0}.", fruit);
    }
    else
    {
        Console.WriteLine("Enum couldn't be parsed.");
    }
}

Működni működik, de elég cifra kód. A Framework 4.0 Base Class Library-jében kapunk egy új, generikus metódust, a TryParse(), amivel jóval egyszerűbbé és érthetőbbé válik a kód:

public static void ParseEnum40(string fruitName)
{
    Fruit fruit;

    if (Enum.TryParse(fruitName, out fruit))
    {
        Console.WriteLine("Enum parsed succesfully, value: {0}.", fruit);
    }
    else
    {
        Console.WriteLine("Enum couldn't be parsed.");
    }
}
Külön szépség, hogy bár az Enum.TryParse() egy generikus metódus (tehát valójában az Enum.TryParse<Fruit>()-ot hívjuk), de a típusparamétert - mivel az ugyanaz, mint a második függvényparaméter, az out fruit típusa -, nem kell kiírnunk, a fordító kiinferálja nekünk.

Mindennek ellenére, hogy most már ilyen rettenetesen könnyű és szép enumokkal dolgozni, azért azt tartsuk észben, hogy az enum-sztring konverzió elég lassúcska, teljesítményérzékeny alkalmazásokban néha rá kell programoznunk, ha nem akarunk odaborostásodni a gép elé.

2010. január 7., csütörtök

.NET Framework 4 poszter

A "fejlesztői poszterek" az internet feltalálása óta valószínűleg a világ leghaszontalanabb dolgai közé számítanak, ennek ellenére szeretjük őket.
Egy fejlesztői szoba osztályokat ábrázoló poszterek nélkül olyan, mint egy lakatosműhely Samantha Fox poszter nélkül: jó, jó, de nem igazán kelt professzionális hatást.

A Framework 4.0-ás verziójának közelgő megjelenésével egyúttal a szabad falfelületek további apadására is lehet számítani: NETFX4-Poster.pdf!

2010. január 6., szerda

Miért pont 1753-01-01?

Vajon miért pont 1753 január 1. a legkorábbi dátum, amit az SQL Server a datetime típusban tárolni képes? For some historical reasons - jelen esetben szó szerint.

Bár az embernek könnyen úgy tűnhet, hogy az, hogy mi most 2010-et írunk, valami egyetemleges reprezentációja az idő múlásának, igazából számos naptár-rendszer létezett, és létezik ma is (az ortodox zsidók szerint pl. most 5770 van).
Európa nagy részén már a mai időszámítás kezdetétől a Juliánusz-naptárat használták, ami körülbelül ugyanaz, mint a ma is használatos naptár, leszámítva, hogy nincsenek benne szökőévek. (update: Voltak benne szökőévek - pont az volt a baj, hogy túl sok. A Julián-naptár éve mintegy 11 perccel hosszabb, mint a tropikus év.) Emiatt viszont lassan "csúszkálni" kezdtek az évszakok, ami nem volt túl kellemes. A tarthatatlan helyzetet megoldandó, XIII. Gergely pápa kidolgozta a Gregorián- (Gergely-) naptárat, amiben egyrész már voltak szökőnapok, másrészt a bevezetésének volt egy kis trükkje, mert az addig összeszedett csúszást át kellett "időugrani".

Nem is volt zökkenőmentes a bevezetés, az országok szinte egyenként vezették csak be. Az angolszász világ pl. 1752 szeptember 4.-én - másnap már egy szép őszi szeptember 14 virradt rájuk. Az 1752 szeptember 14 előtti dátumok kezelése így több szempontból is problémás: egyrészt a hiányzó 10 nap miatt, másrészt kultúrától függően mást-mást jelent egy dátum, meg aztán az se világos, hogy előtte akkor kell-e számolni a szökőévekkel, vagy sem...

A SQL Server fejlesztő a Microso Sybase-nél pedig egy huszárvágással letudták a nehézségeket: nem kezelnek 1753 előtti dátumokat.

Persze voltak államok, ahol még ennél is később vezették be a Gergely-naptárat, a nagy Szovjetúnióban pl. csak az első világháború után - nem véletlenül volt a Nagy Októberi Szocialista Forradalom évfordulója november 7.-én...
By the way, azt tudják, hogy miért 31 napos július meg augusztus is, miközben a február meg ilyen rövidke? Hát mert augusztust Augustus császárról nevezték el, egészen konkrétan ő maga nevezte át a nyilvánvalóan sokkal bénább Sextilius (update: helyesen: Sextilis) névről - viszont ez a hónap csak 30 napos volt, szemben a Julius caesarról elnevezett szomszéd Júliussal, ami meg 31, úgyhogy az amúgy is kicsit rövidebbre sikerült februárból (annak már úgy is mindegy) egy nap le lett csípve, és Sextil augusztushoz adva. Hiába, no, a szerénység gyakran együtt jár az ilyen pozíciókkal!

2010. január 4., hétfő

Klasszikus (módú) szívás

Ha egy webalkalmazásunkban szeretnénk a HTTP response-hoz saját header érték(ek)et fűzni, kézenfekvő(nek tűnik) a megoldás: elérjük a HttpResponse példányunkat, azon van egy Headers kollekció, annak meg van Add metódusa:

Response.Headers.Add("MyHeaderInfo", "XY");

Erre IIS 5 / 6 / 7 Classic Mode alatt egy PlatformNotSupportedException-t kapunk, merthogy "This operation requires IIS integrated pipeline mode.". Mifene, classic módban nem tudunk headereket hozzáadni a response-hoz? Természetesen de, csak másképp:
Response.AddHeader("MyHeaderInfo", "XY");
Vagy használhatjuk az AppendHeader() metódust is, pont ugyanazt csinálja, mint az AddHeader (az AddHeader valójában egyszerűen csak továbbhív az AppendHeaderre, ami a valódi mágiát csinálja, érdemes megreflektorozni).

2009. december 26., szombat

Community Powa

Kerestem fülbe dugható fülhallgatót. Szerencse, hogy az Interneten számos ár-összehasonlító oldal létezik, így a keresgélés jóval egyszerűbb - ráadásul a legtöbb helyen pontozni, kommentezni is lehet az egyes termékeket, így már vásárlás előtt teljes képet kaphat a megvásárolni kívánt portákáról. Találtam is egy elég szimpatikus darabot, a Sennheiser CX 300-as modelljét, nézzük, mit írnak róla!

"Ma vettem és elsőre letaglózott. ...10-es skálán 8-ast adnék neki, de így 5-ös skálán legyen 5-ös. Jó érték-arány, professzionális hangzás. Csak ajánlani tudom!"
Na, hát ez kiváló, bár a 10-es skálán 8-as eredményt ötös skálán ötössé konvertálni elég furcsa, lehet, hogy inkább egy másik értékelő véleményére kéne hallgatni. Nézzük mit ír a következő!
"Egy olcsóbb Panasonic inear-t szerettem volna leváltani vele. Nem sikerült. Annak ellenére, hogy Sennheiser, a hangja borzalmas, egyszerűen sérti a fület. Mindenféle EQ beállítással kipróbáltam, de egyik sem jött be. A közepei üvöltenek, a magas is sok, mélye pedig egyáltalán nincs. Nagyot csalódtam a gyártóban."
Ajaj, hát ezek szerint ez mégsem annyira tökéletes. De várjunk csak, nézzük mit írnak még:
"A legjobb! Ez előtt kb. 20-30 fülest kipróbáltam, de egyik se váltotta be a hozzá fűzött reményeket... Már fél éve használom a CX300-at és bátran kijelenthetem, hogy ez a legjobb ár/érték téren. Gyönyörűen szól! A 25Hz-es szinuszhullámtól, amit ráadtam, azt hittem, hogy fölrobbantja a fejem."
Hát akkor mégis elég jó lehet, bár itt megint számolni azoknak az embereknek a beszámíthatósági faktorával, akik 25Hz-es szinuszt hallgatnak.
Jobb lenne valami zenei műveltséggel bíró személy tapasztalataira... de hoppá, hát itt van!
"zenélek már 16 éve,és ugyan abszolút hallásom nincs,de tökéletes zenei relatív hallásom van,ilyen képességek birtokába mondom erről a fülesről hogy egy minőségi darab.csak akik a sok rosszon éltek előtte,azoknak ez most túl erős /gyenge lehet attól függ ogy előtte milyen fülesük volt. érdemes az eq-val próbálkozni,kiválló hangzást lehet elérni vele."
Na, hát jó ez az írástudók szerint is. Akkor most már biztos, hogy jó lesz. Na még egy véleményt elolvasok...
"Egyszerüen nem szól tisztán. Kár, hogy megvettem. Kásás és a magas az szörnyű. Próba kell mindenképpen mielött megveszed, mert ha valaki erre 16 év zenélés után rámondja, hogy minőségi, akkor tényleg megosztja rendesen az embereket. Az ilyen vélemény maximum egy utalás arra aki zenéhez értőnek tartja magát."
Hajjaj, akkor most mégsem?
"Kitűnő hangzás, kitűnő dinamika, kitűnő kivitelezés! Ezzel lehetne jellemezni a CX300-at! Bődületesen jól szól! Mindenkinek csak ajánlani tudom!"
De, jó ez, kitűnő!
"Nulla a cucc! Sajnálom, hogy megvettem. Ami ebből szól, azt ne nevezzük hangzásnak."
Jajj, nem. Mégsem. Vagy...?
"A Sennheiser cx300 a legjobb ami valaha a fülemben volt!!! Leginkább a munkahelyemen használom a fülest, hogy "gyorsabban teljen az idő"."
Na, akkor most jó ez...
"Egy hifi üzletben volt lehetőségem vásárlás előtt meghallgatni. Ezek után a vásárlás elmaradt!"
Nem, mégsem, vagy...?

És így tovább, a végtelenségig. Műértők szakvéleményei, majd a műértők hozzá nem értését leleplező műértő írások, szépen, sorban, egymás alatt, a személyeskedő anyázás határmezsgyéjét még épp át nem lépve (amivel ez a fórum egyébként a kultúráltabbak közé pozícionálódik).
Ráadásul a kis füldugasz ára elég szép szórást mutat, 6.734 Ft-tól kerek 12.000 Ft-ig kapható, ami a következő, a széles nyilvánosság számára elérhető felületeken óhatatlanul felbukkanó kaszt, a beavatottak számára olyan magas labda, amit nem lehet nem lecsapni. Megtudhatjuk, hogy ezt a modellt hamisítják, sok helyen a boltokban is hamisítványt kapni, 8000 alatt biztos, afölött meg ki tudja, hát ki, ki más tudná, mint a nagybetűs FÓRUMOZÓ, csak szét kell bontani a borítást, és lesz ott egy szám, ami ha négyessel kezdődik, akkor eredeti, ha hatos, akkor hamis, és mint ilyen, kerülendő, sz*r, értéktelen szemét.

Végül az lett, hogy legutóbb, mikor a szokásos ünnepek előtti nagybevásárlást intéztem egy hyperben (fel kellett tölteni az égetettszesz-készleteket), leakasztottam a szögről egy Panasonic fülest, 1990 Ft, nem egy audiophile cucc, de a célnak megfelel, az Asian Dub Foundation egész meggyőzően dörren meg rajta reggel a négyeshatoson.
Mi lett volna velem a Közösség nélkül? Valószínű egy háromszor (esetleg négyszer, ötször, vagy hatszor) ilyen drága fülest tömködnék reggelente a fülembe, aztán gondolkozhatnék erősen, hogy most amit hallok, az tetszik-e nekem, vagy sem.

Community power, köszönöm!

2009. december 18., péntek

Kovariancia és kontravariancia C# 4.0-ban

Miről beszélünk?

C#-ban egy referencia típusú változó mindig a saját típusára, vagy az abból leszármazott típusra mutathat. Ez teljesen transzparens, amikor egy object referencián meghívsz egy metódust, mindegy, hogy a referencia tényleg egy object-re, string-re, Kiskutya-ra mutat-e. Ezzel valószínűleg mindenki találkozott már az első "Objektum-orientált programozás" órán, valamint azóta kb. egycsilliárdszor.
Nevezzük ezt mondjuk "egyes számú szabály"-nak.

Ha két típust (mondjuk T-t és U-t) egymáshoz hasonlítunk, az alábbi négy lehetőségből pontosan egy lesz igaz:

  • T nagyobb, mint U (OOP terminológiával: T őse U-nak - a System.Object őse a System.String-nek, tehát az object nagyobb, mint a string)
  • T kisebb, mint U (OOP terminológiával: T leszármazottja U-nak - a System.String a System.Object leszármazottja, tehát a string kisebb, mint az object)
  • T egyenlő U-val (OOP terminológiával: T és U ugyanaz a típus)
  • T-nek nincs kapcsolata U-val (OOP terminológiával: navajon?)

Vegyünk egy műveletet, ami a típusokkal mókol valamit, egy adott típusból egy másikat csinál valamilyen szabály alapján: T-ból T'-t, U-ból U'-t.

Hogyha a T' ugyanolyan relációban lesz U'-vel (a fenti négyből), mint T volt U-val, akkor a művelet kovariáns. Ha a művelet megfordítja a relációt (pontosabban: a kisebbséget-nagyobbságot megfordítja, az egyenlőséget és a "közömbösséget" változatlanul hagyja), akkor a művelet kontravariáns.

Érthetetlen, ugye? Nézzünk egy példát!

Adott két típus, mondjuk a System.Object és a System.String (közülük az object a "nagyobb"). Most vegyünk egy "műveletet", ami csinál valamit a típusokkal. Ilyen művelet például a "képezzünk T tömböt", vagy a "képezzünk T listát". Lesz tehát object[] (aka. T'), string[] (aka. U'), List<object> (legyen mondjuk T''), List<string> (logikusan U'') típusunk.

Milyen viszonban vannak ezek a típusok egymással? Az object[] nagyobb, mint a string[]? Hát nézzük meg, az egyes számú szabály értelmében, ha az, akkor értékül lehet neki adni:

// C# 1.0 kód
string[] stringArray = new string[] { "http://", "otperc", ".net" };
object[] objectArray = stringArray;

A C#-ban a tömbképzés már a nyelv legelső verziója óta kovariáns.

És a List<string> castolható List<object>-té?

// C# 2.0 kód
var stringList = new List<string> { "http://", "otperc", ".net" };
var objectList = (List<object>)stringList; // ez nem fordul!

Erre bizony még a nyelv 3.0-ás változatában is egy "Cannot convert type 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.List<object>'" a fordító válasza.

Veszélyes utakon

De vajon ha a tömböknél meg tudták oldani a dolgot már az 1.0-ban, akkor miért nem ment a genericnél a másodiknál? Eric Lippert azt mondja erről: "az a mód, ahogy a C# támogatja a tömböknél a kovarianciát, broken.
Azért került be a CLR-be, mert a CLR tervezői képessé akarták tenni azt a Java(-szerű) nyelvek támogatására, és ehhez szükség volt rá. Aztán mi beraktuk a C#-ba, ha már a CLR-ben bent volt. Sokat vitatkoztunk ezen a döntésen, és ma már nem örülök neki túlságosan, hogy így alakult, de most már nem sok mindent tudunk csinálni.
"

Miről beszél Lippert? Lássunk egy példakódot:

private static void GetDataMethod(object[] objectArrayParam)
{
    foreach (var item in objectArrayParam)
    {
        Console.WriteLine(item);
    }
}

private static void SetDataMethod(object[] objectArrayParam)
{
    objectArrayParam[0] = new object();
}

static void Main(string[] args)
{
    string[] stringArray = new string[] { "http://", "otperc", ".net" };
    object[] objectArray = stringArray;

    GetDataMethod(objectArray);
    SetDataMethod(objectArray);
}

Az első metódus lefut, de a második futtatásakor egy "ArrayTypeMismatchException: Attempted to access an element as a type incompatible with the array." üzenetet kapunk. A hangsúly a futtatásakoron van: sikerült egy erősen típusos nyelvbe egy olyan konstrukciót beemelni, ami remek lehetőséget ad a compile-time típusbiztosság-ellenőrzés kikerülésére - amivel pedig megnyílnak a pokol kapui.

A helyes út

A C# 2.0-tól van olyan variancia-támogatás is a nyelvben, ami nem “broken”, méghozzá a generikus delegate-ek:

private static string GetString()
{
    return "http://otperc.net";
}

static void Main(string[] args)
{
    Func<object> functionThatReturnsObject;
    functionThatReturnsObject = GetString;
    object o = functionThatReturnsObject();
}

A Func<T> egy T típust visszaadó metódust reprezentál - tehát a Func<object> egy olyat, ami object-et ad vissza. Ez a "metódusreferencia" bátran mutathat olyan metódusra, ami string-et ad vissza. Mi baj lehet? Semmi. A hívó ugy is object-et vár, nem érheti meglepetés. Bármi olyanra mutathatunk, ami specializáltabb, mint mi (az object-es példa esetében praktikusan bármire). Visszafele viszont nem megy a dolog, ha a hívó string-et vár, nem adhatunk object-et.

A generikus delegate-ek a castolása visszaadott típusokra nézve kovariáns.

Nézzük a másik esetet, mikor a típusparaméter nem a visszaadott típust, hanem a paraméter típusát mondja meg. Az Action<T> delegate egy visszatérési érték nélküli, egy T típusú paramétert váró metódust reprezentál. Ha egy metódus mondjuk object-et vár, bátran elérhetjük egy olyan Action-ön keresztül, ami string-et (vagy bármi már specializáltabbat) vár, mert az "alatta lévő", object-et váró metódus simán meg fogja enni:

private static void ConsumeObject(object obj)
{
    Console.WriteLine(obj);
}

static void Main(string[] args)
{
    Action<string> methodThatConsumesString = ConsumeObject;
    methodThatConsumesString("http://otperc.net");
}

A generikus delegate-ek castolása a paraméterek típusára nézve kontravariáns - a típusbiztosság pedig mindkét esetben compile-time ellenőrzött.

Variancia a C# 4.0-ban

Eddig láttunk példát a kontra- és kovarianciára is, annak jó és kevésbé jó implementációjára tömböknél, delegate-eknél. Egy valamit nem láttunk általános generikus típusoknál, pl. egy List<T>-nél a variancia használatára. Azért nem láttunk, mert nincs. C# 3.0-ig nem volt lehetőségünk mondjuk egy List<string>-et List<object>-té castolni - C# 4.0-tól majd lesz. Bizonyos esetekben.

Nem meglepő, hogy ezt a fajta támogatást a nyelvbe "nem-broken" módon igyekeznek behozni - a klasszikus kecskés-káposztás felállásban, miszerint használhassunk kontra- vagy kovariáns hozzárendeléseket ott, ahol azok működőképesek és hasznosak, de maradjon meg a fordításidejű típusbiztosság-ellenőrzés is. Hogy tudjuk szétszeparálni az működő eseteket a nem-működőktől? Nézzük meg újra a legelső példakódunkat:

GetDataMethod(objectArray); // ez a metódus lefut
SetDataMethod(objectArray); // ez ArrayTypeMismatchException-t dob

Miért tud futni az első, és miért nem a második? Mi a különbség?
Mondjuk első közelítésben az "adatáramlás iránya". Az elsőben csak iterálunk, "kiveszünk" adatokat, míg a másodikban "berakni" próbálunk.

A delegate-es példánál is, a visszatérési érték típusa ("kimenő irány") kovariáns - ami X típust ad vissza, az visszaadhat X-et, vagy bármi specializáltabbat. A másik irányba meg fordítva.

A C# 4.0-ba két új kulcsszó került be a variancia támogatására: az in és az out varianciamódosítót (jó, egyikük kulcsszó-pályafutásának sem ez a kezdete, de ez most egy új szerepkör). Ezzel a két kulcsszóval generikus interface-ek típusparamétereit jelölhetjük meg, mint "bemeneti" (kontravariáns) vagy "kimeneti" (kovariáns) paraméter.

Egy példa talán érthetőbbé teszi a dolgot: vegyük például az IEnumerable<T> generikus interfészt. Egy ilyen osztályból T típusú elemek "jönnek ki" - tehát T-nél "nagyobb", általánosabb elemeket is kivehetünk. Egy IEnumerable<string> vígan tud IEnumerable<object>-ként is viselkedni. Bajt nem csinálhatunk, mert "befele" nem megy adat, nem fordulhat elő, hogy egy, magát IEnumerable<object>-nek mutató IEnumerable<string>-be egy int típusú adatot rakunk, merthogy az object megbírja azt is.

A .NET Framework 4.0-ban ennek az interfésznek így néz ki a definíciója:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Azzal, hogy ki lett rakva az out, a T paraméter kovariánssá vált:

// C# 4.0 kód:
IEnumerable<string> enumerableOfString =
    new string[] { "http://", "otperc", ".net" };
IEnumerable<object> enumerableOfObject = enumerableOfString;

Annak pedig, hogy az out-ot ki lehetett rakni, egy előfeltétele volt: a T csak a "kimeneti oldalon" szerepelt az interfész definíciójában.
Most nézzünk meg egy másik interfészt:

public interface IComparer<in T>
{
    int Compare(T x, T y);
}

Itt most az "in" módosítót használták, a T paramétert kontravariánssá téve - talán most már nem túl meglepő a felfedezés, hogy a T csak bemenő paraméterként használt. T helyére bepasszol T, vagy bámi nála specializáltabb: egy comparer, ami össze tud hasonlítani két object-et, két stringet is össze tud.

Természetesen az in és az out típusparaméterek használata nem csak a framework fejlesztők kiváltsága, mi is használhatjuk őket a saját interfészeinkben.

Osztályokon nem, azt egy "Invalid variance modifier. Only interface and delegate type parameters can be specified as variant." üzenettel jutalmazza a fordító. A variancia ugyanis műveleteken értelmezett, a műveleteket pedig leginkább a delegate-ek és az interfészek írják le. Az List<T> típusparaméterére nem rakhattak ilyen módosítót, mert a T ki- és bemenő oldalon is használt. Viszont az List<T> egyben IEnumerable<T> is (illetve IEnumerable<out T>), úgyhogy ha "azt az arcát mutatja", akkor használhatjuk úgy.

Szintén ellenőrzi a fordító, hogy ha in vagy out varianciamódosítót használsz, akkor a típusparamétered tényleg csak a megfelelő helyen bukkanjon fel, out paramétert bemenőként használva "Invalid variance: The type parameter 'T' must be contravariantly valid on '<metódusnév>'. 'T' is covariant." üzenetet kapunk, in-t kimenőként használva pedig "Invalid variance: The type parameter 'T' must be covariantly valid on '<metódusnév>'. 'T' is contravariant."-et.

A teljes és kendőzetlen igazság

Be kell vallanom: a fenti postból sok minden nem igaz. Illetve igaz, csak nem úgy. Néhol egyszerűsítésekkel éltem, pl. a “kisebbség-nagyobbság”, “hozzárendelhetőség” és a “leszármazás” fogalmait illene jobban tisztába rakni. Belátható, hogy a kettő nem ugyanaz, mert bár fentebb láttuk, hogy az IEnumerable<object>-nek símán értékül adhatjuk az IEnumerable<string>-et, ez az “egyes számú szabály” értelmében azt is jelenti, hogy az IEnumerable<string> öröklési láncában (bocs, megint pongyola vagyok, interfészről beszélünk: öröklési fájában) valahol szerepelnie kéne az IEnumerable<object>-nek. Egyet biztosíthatok: nem szerepel.

Ha ezek a részletek is a helyükön lennének, akkor korrekt lenne az írás, viszont érthetetlen. A neten rengeteg anyag fellelhető, but I won’t bring it to you – you have to bing it for yourself. :)

2009. december 10., csütörtök

Összes tábla összes adatának törlése SQL Serverben

Fejleszési-tesztelési időben előfordul, hogy szeretnénk "nyomni egy resetet" az adatbázisunkon: a séma marad, de ki szeretnék dobni minden adatot. TRUNCATE-eljünk mindent!

-- disable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? NOCHECK CONSTRAINT all"

-- delete data in all tables
EXEC sp_MSForEachTable "DELETE FROM ?"

-- enable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

A középső sor a tényleges truncate, a kezdő-záró ALTER TABLE-ök csak a kényszerek betartatását kapcsolják ki, majd vissza - így a FOREIGN KEY-ek sem akadályozzák meg az adatok törlését.


az eredeti script és további bölcsességek a témában:
How do you truncate all tables in a database using TSQL? @ StackOverflow

az sp_MSforeachtable tárolt eljárás további hasznos felhasználási lehetőségei:
8 Common Uses of the undocumented Stored Procedure sp_MSforeachtable @ SQLServerCurry

2009. december 7., hétfő

Forráskód másolása HTML-ként Visual Studio-ból

Ha valamilyen technikai irományt (dokumentációt, blogbejegyzést, stb...) készítünk, gyakran lehet szükség arra, hogy abba forráskódot illesszünk be. Ez még nem agysebészet, Visual Studióban kódrészlet kijelöl, CTRL+C, CTRL+V, és kész, átmegy a szöveg.
Egy dolog viszont nem megy át: az a gyönyörű syntax highlight (na jó, Word-be átmegy). Pedig a Studio kódkiemelős funkciója remek, jó lenne azt úgy például HTML-ként kiemelni.

Pont ezt tudja a CopySourceAsHtml nevű Visual Studio add-on, amit ingyenesen le lehet tölteni a http://copysourceashtml.codeplex.com/-ról.
A 3.0-ás verzióban már egy .msi installert kapunk, next-next-finish telepítéssel. Utána egy egy VS újraindítás, és a File, Edit menükben, illetve a kijelölt szöveg context menüjében egy új elem bukkan fel: a Save/Copy as HTML.
Ha már vannak saját CSS stílusaink, azok használatára is beidomíthatjuk.

2009. december 5., szombat

Nullérték-vizsgálat egyszerűen

Eredeti (Java-s) ötlet itt: http://cesjava.freeblog.hu/archives/2009/10/13/a_NUllPointerException_transzparens_kezelese/.

Saját továbbgondolás (C#):

public static class NullReferenceCheckExtensions

{

    public static void CheckIfNull<T>(this T instance)

        where T : class

    {

        if (instance == null)

        {

            string argumentInfo = TryGetArgumentInfo();

 

            if (argumentInfo == null)

                argumentInfo = string.Format("An instance of type '{0}'.", typeof(T).FullName);

 

            throw new ArgumentNullException(argumentInfo);

        }

    }

 

    private static string TryGetArgumentInfo()

    {

        try

        {

            var st = new System.Diagnostics.StackTrace(true);

            var callerFrame = st.GetFrame(2);

            var fileName = callerFrame.GetFileName();

            var fullSource = System.IO.File.ReadAllLines(fileName);

            var info = fullSource[callerFrame.GetFileLineNumber() - 1].Trim();

            return info;

        }

        catch

        {

            return null;

        }

    }

}

Használat:

public void MyMethod(string name, Uri uri)

{

    name.CheckIfNull();

    uri.CheckIfNull();

}