Daniel Kolman

Co by měl každý programátor vědět

| 18 comments |

Občas mě zarazí, že i lidi, které považuju za dobré programátory, neznají základy programovacího jazyka, ve kterém píšou 95% kódu. Například nedávno v naší kanceláři proběhla debata, proč si StyleCop stěžuje na to, že jsou v kódu nadbytečné závorky. Šlo o složenou podmínku v příkazu if, kde si dotyčný pomáhal závorkami, protože si nebyl 100% jistý s prioritou operátorů && a ||. Co na to říct?

Podle mého jsou určité základy, které musí každý programátor o svém jazyce vědět. Priorita operátorů podle mně patří mezi ně. Argument, že v jazyce XX nebo YY to bylo jinak, je podle mně zcestný. Programovací jazyky jsou natolik jednoduché, že programátor nemůže mít problém si těch pár věcí zapamatovat. Netvrdím, že musíte z hlavy vědět vše o nějakém obskurním jazyku, který používáte jednou za uherský rok, ale pokud se někdo už několik let živí psaním kódu v C#, musí takové věci znát bez zaváhání.

Co všechno by jste tedy měli vědět o svém hlavním programovacím jazyku?

  • Priorita operátorů (!, ++, ||, &&, ?:)

  • Jak zapsat hodnotu různých datových typů (0xAF, 123 a 123L, 0.12M a 0.12D)

  • Základní konverze na string a zpět (například donedávna jsem považoval za samozřejmost, že každý ví jak konvertovat "FFEEDD" na int, ale byl jsem vyveden z omylu)

  • Speciální jazykové konstrukce jako anonymní delegáti a lambda funkce



Tento seznam určitě není kompletní, pokud vás napadne něco dalšího, napište to do komentářů.

Pokud tyto základní věci neovládáte, riskujete dva velké problémy: Budete se pomalu orientovat v kódu ("jakého typu je var a = new[] { 1, 10, 100, 1000 };?"), a při psaní budete používat zbytečné ornamenty (if(((age>60)||(disabled==true))&&(balance>limit))). Druhý problém je o dost horší než první, protože při čtení zpomalujete jen sami sebe. Pokud ale kvůli své neznalosti použijete o 8 závorek navíc, ztěžujete práci ostatním, protože znepřehledňujete kód. Je sice pravda, že ostatní pak nemusí používat svn blame, protože už znají váš styl:-) Být poznat podle stylu kódu ale není nic o co by jste měli stát. A taky budete hodně nadávat na StyleCop:-)))

Pokud neovládáte ani naprosto základní věci, jak chcete přesvědčit ostatní o tom, že jste dobří programátoři?

Co všechno musíte vyřešit při psaní WPF aplikace

| 0 comments |

WPF je kůl, o tom žádná. Proti Windows Forms je to opravdu obrovský skok kupředu. Ale to ještě neznamená, že při tvorbě aplikace založené na WPF nemůžete dostat osypky a že vás nečekají bezesné noci. V tomto příspěvku se pokusím shrnout hlavní problémy, ke kterým je třeba se postavit čelem, bez nároku na nalezení konečného řešení:

  • INotifyPropertyChanged

  • Validace

  • Lokalizace

  • Undo/Redo


INotifyPropertyChanged


Aby View vědělo kdy se má updatovat, je nutné u všech ViewModel objektů implementovat rozhraní INotifyPropertyChanged. Naštěstí je velmi jednoduché, ale budete muset psát spoustu opakovaného kódu:

string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name);
}
}

Vyhnout se mu můžete buď generováním kostry ViewModelu pomocí DSL jazyka, nebo pomocí AOP (kolegové používají PostSharp), kde stejná property může vypadat takto:

[Notify]
public string Name { get; set; }

Lokalizace


Existuje několik přístupů k lokalizaci WPF aplikace, které se liší způsobem uložení resource stringů, možností výběru lokalizovaných vlastností a možností přepínat jazyk "za běhu", bez nutnosti restartu aplikace. Řešení které doporučuje Microsoft (přes binární XAML, neboli BAML) potřebuje externí nástroj LocBaml.exe, editovat jazykové verze musíte v CSV a při přepnutí jazyka za běhu se nelokalizují již otevřená okna. Které vlastnosti budou lokalizované se dá určit až při lokalizaci, takže překladatel se může rozhodnout, že např. sloupec v tabulce bude mít jinou velikost nebo label se zarovná na jinou stranu. To může být výhoda i problém, záleží na vašich potřebách. Další dva způsoby naleznete v článku Localizing WPF Applications using Locbaml, oba ale neumožňují přepínat jazyk za běhu.

Zajímavé řešení je popsáno v WPF Localization - On-the-fly Language Selection. To už umožňuje skutečné přepnutí jazyka bez nutnosti restartu aplikace. Je založené na vlastním Converteru, který provádí lokalizaci, díky čemuž lze používat poziční parametry v resource stringu ("{0}") a jejich hodnoty dokonce bindovat. Trik umožňující okamžité přepnutí jazyka spočívá v nahrazení lokalizovaných hodnot MultiBinding objektem, kde první vazba je na LanguageContext.Instance.Dictionary, která při změně jazyka posílá událost PropertyChanged. To způsobí znovunačtení všech hodnot. Tomuto řešení bych vytknul pouze způsob uložení resourců (vlastní XML), ale velmi lehce si můžete napsat vlastní LanguageDictionary a číst hodnoty např. z resx souborů.

Validace


Při prvním pohledu na WPF se zdá, že validace je vyřešená věc, ale pokud chcete použít vzor MVVM, není to tak jednoduché. Ve ViewModelu tedy implementujeme IDataErrorInfo a projdeme všechny deklarace Binding v XAMLu a přidáme ValidatesOnDataErrors=true.

Jenže kromě business validací je nutné také validovat vstup od uživatele. To jsou opravdu dvě různé věci: Mějme třídu Person s property Age typu int. "Business" validace je, že Age nesmí být záporné a musí být menší než 150. Když je hodnota mimo tento rozsah, vrátíme přes IDataErrorInfo chybovou hlášku. Jenže co když uživatel zadá "qweqweqwr"? Výchozí chování Bindingu je výjimku prostě spolknout. Můžeme nastavit ValidatesOnExceptions na true, ale jednak nemůžeme změnit validační hlášku, a také nechceme žádné výjimky. Výjimky jsou pro výjimečné případy, např. když je vyhlášen výjimečný stav. Spoléhání na výjimky může aplikaci znatelně přibrzdit.

WPF Binding zpracovává vstup přes řetěz validátor - konvertér - property setter. Možným řešením by bylo vkládat vlastní validátory do deklarace Bindingů. To ale odporuje našemu cíli, dostat všechnu logiku do ViewModelu. Navíc až budeme objekt ukládat (uživatel zmáčkne Ctrl+S), nedozvěděli bychom se, že na formuláři jsou nevalidní data. Informaci o validitě si přece chceme přečíst z ViewModelu. Nejen kvůli tomu odpadá i další možnost: generovat validátory podle metadat ViewModelu. Ani to nezkoušejte, nejde to. Jak zjistil Miro, Binding je jen MarkupExtension, který vytvoří BindingExpression, který se teprve stará o update hodnot, jenže většina jeho důležitých vlastností je internal.

Jediným mně známým řešením je proto deklarovat ve ViewModelu property Age jako string a provádět konverzi až tam. Když se konverze povede, zapíšete property do Modelu, kde už je typu int a kde se provede business validace. Je to víc práce ale zase má ViewModel informaci o tom, jestli je formulář validní. Implementace je popsaná v článku Using a ViewModel to Provide Meaningful Validation Error Messages.

Aby to nebylo tak lehké, představte si že chcete z ViewModelu vrátit hlášku "Věk musí být mezi {0} a {1}". Obstarožní interface IDataErrorInfo umožňuje vracet pouze string, takže ve ViewModelu provedeme String.Format a vrátíme už hotový text. Jenže to komplikuje lokalizaci. Kvůli přepnutí jazyka za běhu bychom potřebovali vracet klíč do resourců a pole parametrů, aby lokalizátor mohl aktualizovat hlášku. Ideální by tedy bylo napsat si vlastní interface, lepší a hezčí než IDataErrorInfo (to by věru nebylo těžké), protože do ValidationError objektu můžeme ukládat daleko více informací než jen string. Pomocí Reflectoru kolega Miro zjistil, že Binding při nastavení ValidatesOnDataErrors=true vkládá do pole validátorů objekt DataErrorValidationRule. Kdybychom tedy napsali vlastní ValidationRule, mohli bychom přečíst errory pomocí vlastního interface a vytvořit takový ValidationError objekt, který by byl lokalizátor schopen za běhu překládat. Jenže opět narážíme na to, že property BindingExpression.ItemSource, která obsahuje zdroj data bindingu (ViewModel), je internal! Zbývá nám tedy buď oželit okamžitou lokalizaci validačních hlášek, přinutit všechny BindingExpression objekty provést refresh, nebo si do stringu serializovat template s parametry a v lokalizátoru je deserializovat, vše mi přijde ošklivé.

Undo/Redo


WPF má sice vestavěnou podporu pro undo/redo v TextBoxu, ale to nám samozřejmě nestačí, potřebujeme undo/redo v celé aplikaci (nebo lépe, v celém editovaném projektu, který tvoří undo scope - projektů můžeme mít otevřených víc naráz). Zde se nabízí klasické řešení pomocí UndoItem objektu a undo a redo stacku, popsané třeba zde. Je ovšem nutné vyřešit otázku jak nalézt undo scope (projekt), když naše ViewModel objekty tvoří košatě rozvětvený strom.

Také musíme implementovat undo transakce. Kvůli tomu, aby bylo uživatelské rozhraní živější, jsme totiž ve všech Binding deklaracích nastavili UpdateSourceTrigger=PropertyChanged. Díky tomu je uživatelův vstup okamžitě validován, ale protože se přes ViewModel posílá každý jednotlivý znak který uživatel zadá do TextBoxu, bylo by undo dost nepoužitelné. Potřebujeme tedy zahájit undo transakci při vstupu do TextBoxu a ukončit ji při jeho opuštění (nebo stisku enter). To nás nejen odsuzuje k handlování událostí nebo vlastnímu TextBoxu, ale má to i jeden háček: Při kliknutí myší do menu nebo na toolbar TextBox neztratí focus. Tím pádem se při Save/Load neukončí undo transakce a nezahájí se nová, na což je potřeba myslet a modelovat root undo scope (projekt) tak, aby ho bylo možné při Load celý zahodit a vytvořit nový (i pak si ale TextBox drží focus a nezahájí novou transakci).

Závěr


Jak je vidět, komplexitu dobré aplikace není radno podceňovat ani s tak vyspělým frameworkem, jako je WPF. Pokud narazíte na další problémy nebo na jiná řešení, dejte vědět.

Architektura Ginger

| 2 comments |

Ginger je náš framework pro webové aplikace. Tvoří ho knihovny s infrastrukturním kódem a sada doporučení (nebo spíš přikázání:-))), jak rozdělit aplikaci na vrstvy a které projekty umístit do které vrstvy. Je určen pro webovou vícevrstvou škálovatelnou aplikaci, která ukládá data do databáze.

Trochu teorie kolem DSL

| 1 comments |

Doufám, že vás v mém posledním postu moc nevystrašilo slovní spojení "Domain-Specific Language". Je to jen další buzzword: zní to sofistikovaně, ale ve skutečnosti to není nic nového. Laskavý čtenář, kterému rostou víc vousy než vlasy, si možná vzpomene na minijazyky v unixu, což není nic jiného než DSL - malý jazyk vymyšlený pro konkrétní účel.

O čem bych chtěl psát ale nemám na to čas

| 0 comments |

Od mého posledního ublognutí uplynulo mnoho LOC. Není to tím že bych neměl o čem psát, ale nějak se mi nedostává času. Tak alespoň ve stručnosti, na čem pracuju a o čem bych si v budoucnu chtěl ublognout:


  • Codename Ginger: Aplikační framework pro tvorbu distribuovaných (nejen) webových aplikací. Řeší architektonický základ takové aplikace: rozdělení do vrstev a vrstev (v češtině se dost špatně rozlišuje layer a tier), jazyk pro modelování domény vytvořený pomocí DSL Tools, generování různých artefaktů jako skriptů pro vytvoření databáze, mapovacích souborů, doménových a DTO objektů přímo z modelu pomocí T4. Není to žádná chiméra, máme reálně fungující a solidně protestovanou verzi a vyvíjíme v tom komerční aplikace. Na webové vrstvě používáme ASP.NET MVC, na datové NHibernate a trochu jsme to okořenili Spring.Net (ale jen s citem). Velkou inspirací pro nás je NServiceBus a pak samozřejmě bible enterprise aplikací Patterns of EAA od Martina Fowlera.


  • WPF: Celkem dlouho jsem tuto technologii ignoroval, protože jsem někde četl že v ní není DataGrid. Hrozná chyba! Příliš mnoho času jsem strávil ve Windows Forms, takže jsem si nedokázal představit, jak flexibilní může být UI framework. Teď si to vynahrazuju. Jsem nadšen ze stylování, data bindingu a content modelu. Kromě toho, že v tom nejdou kloudně zobrazovat bitmapy, je to naprosté programátorské blaho.


  • Scrum: Snažím se v práci prosadit, abychom v příští verzi našeho produktu nasadili tuto metodologii. Máme trochu problém s plánováním, termíny a komunikací, a od Scrumu si slibuju, že budeme mít přesnější představu jak na tom jsme, zpřesníme plánování, díky projektovým tymům usnadníme komunikaci, vývojáři se budou moct lépe soustředit na výsledek a díky tomu také zvedneme kvalitu našeho kódu. Nehledě na to, že nás to bude víc bavit:-) Teorie je na webu dost, ale našel jsem super knížku, která se věnuje praktické realizaci. Je zdarma, tak neváhejte a stahujte, je opravdu dobrá. Doufám že to vyjde a že budu moct časem ublognout jak nám to jde.




Tak to je ve zkratce všechno, mám v hlavě sice daleko víc nápadů, ale den má jen 24 hodin:-)

Jak programovat bez ega

| 5 comments |

Přestože mnozí věří, že programování je exaktní věda, ve skutečnosti má blíž k literatuře. Program se nedá vypočítat, program se musí napsat. Programátor musí, stejně jako spisovatel, psát jazykem kterému rozumí ostatní programátoři a dodržovat určitá stylistická pravidla. Počítačové jazyky totiž existují kvůli lidem a ne kvůli počítačům, ty by se spokojily s nulami a jedničkami. Větší pozornost je sice nutné věnovat kritickým (napínavým) úsekům, ale pokud autor ztratí ze zřetele soudržnost celku, výsledkem bude škvár, který sice možná bude sdělovat příběh, ale prokousat se jím bude peklo.

Na rozdíl od spisovatelů jsou však programátoři nuceni pracovat v týmech. Mají tak problémy, kteří spisovatelé vůbec neznají. Některé jsou technického rázu a jdou celkem uspokojivě vyřešit, např. jak spojovat kusy napsané různými autory do jednoho celku (VCS). Horší vznikají tím, že programátoři jsou lidi. Lidská interakce je nesmírně složitá a ke strojové přesnosti a neutrálnosti má na hony daleko. Představte si to, kvůli mezilidským vztahům dokonce vzniklo několik vědních oborů (sociologie, psychologie, psychiatrie, politologie, ekonomie atd.)! Manažeři programátorů se navíc často domnívají, že se pohybují ve výhradně technické oblasti, a tak mohou problémy druhého typu v klidu nepovšimnuty bujet a stávat se patologickými. Ale to je jiná kapitola.

Dnes se chci věnovat jedné vlastnosti, kterou mají programátoři se spisovateli společnou. Každý autor, ať už píše cokoliv, se se svým dílkem ztotožňuje. Je to naprosto přirozené: Trávíte s ním spoustu času, stojí vás mnoho úsilí a chcete aby se mu dařilo co nejlépe. Když pak přijde kritika, ať už oprávněná nebo ne, je těžké ji přijmout s chladnou hlavou. U spisovatelů to vede k úsměvným historkám ve společenských časopisech, u programátorů k frustraci a nepřátelství. A protože se programátoři potkávají v práci každý den, je to pro ně daleko horší problém než pro spisovatele. Pak je ale těžké napsat dobrý program.

Jak tedy programovat bez ega? V první řadě musí člověk přijmout fakt, že se může mýlit. Vždy se najde někdo, kdo o daném problému ví víc než vy. Zvlášť když strávíte se svým produktem delší dobu, je těžké najít odstup a snadno pak přehlédnete i očividné chyby. Pravdu naopak mohou mít i lidé, od nichž by to člověk vůbec nečekal. A tak nezbývá než věnovat pozornost každé výtce, ať už pochází z jakéhokoliv zdroje.

A opačně, pokud kritizujeme, snažme se nedotknout se osobnosti autora kódu. Kritizujte kód, ne jeho autora. Snažte se navrhovat, jakým způsobem lze kód vylepšit. Pokud je to možné, opřete se o fakta (standardy, měření, literatura, články na webu, dohodnuté konvence atd.). Destruktivní kritika typu "seš snad úplně blbej" nebo "všechno stojí za hovno" nikam nevede, jen šíří pocity zoufalství. Zkuste odhadnout, kolik toho ten druhý "skousne" a buďte hodní na kolegy, kteří nemají takové sebevědomí. To, že má někdo méně zkušeností nebo znalostí neznamená, že je horší než vy. Navíc za rok může být situace klidně opačná. Pokud tým funguje, jeho členové jsou schopni se navzájem učit a mají ze spolupráce dobrý pocit.

Aby se programátoři nestyděli zeptat se kolegy o radu, musí v týmu panovat přesvědčení, že mýlit se je lidské a že víc hlav víc ví. Každý má občas špatný den a jediné co se s tím dá dělat, je umožnit, aby se na chyby přišlo co nejdříve. Dobrou pomůckou mohou být techniky z extrémního programování, jako párové programování, code review, kolektivní vlastnictví kódu a stand-up meetingy (také známé jako scrum). Mám ale obavu, že prvotní je dobrý výběr lidí; nehledět při náboru pouze na technické znalosti, ale i na to, zda je přijímaný člověk týmový hráč. Pokud máte tým složený ze samých "hyper-senzitivních code-machines", žádné metodologie vám asi nepomůžou.

Já sám osobně mám pocit, že už dokážu obstojně snášet kritiku kódu, který jsem napsal. Kritizovat kód kolegů mně ale stále nejde tak, jak bych si představoval. Je těžké vybrat správná slova, aby druhý pochopil, že ho nechci urazit ani snížit jeho zásluhy. Někdy mě zradí jazyk, někdy se moc rozohním, někdy toho mám prostě dost a někdy zafunguje ponorková nemoc, protože pracuju ve firmě, kde jsou trvalé týmy - roky sedíte v kanceláři se stejnými lidmi. Tímto se proto omlouvám každému, koho jsem kdy mohl urazit a chci aby jste věděli, že kašlu na to kdo je autorem, zajímá mě pouze kód a aby byl dobrý.

Ještě poznámka na závěr: Zajímavá je souvislost s jiným principem efektivního programování: Ukazuje se totiž, že programovat bez ega znamená napsat pouze tolik, kolik situace vyžaduje - ne méně a ne více. Nemá prostě cenu vymýšlet všechny varianty, které by po nás mohl uživatel teoreticky chtít, a potom napsat nějaké úžasně univerzální über-řešení; lepší je napsat program, který řeší pouze aktuální problém, a pokud bude potřeba, bude se průběžně doplňovat a zesložiťovat. Je to staré pravidlo YAGNI (You Ain't Gonna Need It), které jinými slovy říká, že i když jste bůhvíjak dobří programátoři, nesmíte se bát obyčejných jednoduchých řešení - není to ostuda.

Egoless Programming
Ten Commandments of Egoless Programming

Ornament a kód

| 1 comments |

Ornament je zločin, vytanula mi na mysl slova Adolfa Loose, když jsem brousil starou olouhovanou skříň. Má totiž několik ozdobných vlnek, které bylo potřeba pracně ručně obrušovat každou zvlášť. A pak mě napadlo, že to vlastně platí víc v programování než v architektuře.

Nejdřív mi dovolte stručné vysvětlení úvodního citátu (čtenáři s humanitním vzděláním laskavě prominou):

Moderní architekti prohlásili dům za stroj na bydlení (*1). Architektura podle nich není umění, ale věda o bydlení, která má upravit hmotné prostředí potřebám lidského živočicha. Nenávist k ornamentu je logickým důsledkem tohoto názoru - vždyť k čemu jsou lidem propracované ozdobičky, když vše co potřebují je čerstvý vzduch, dostatek slunce, teplo a bydlení v obytné zóně bez továren? "Less is more" (Mies van der Rohe) se stalo heslem několika generací architektů. Le Corbusier řekl: „Dekorace je smyslné a primitivní povahy, stejně jako barva, a hodí se toliko pro nižší třídy, sedláky a divochy.“



copyright Jeffery Howe


Adolf Loos i Le Corbusier sice zapomněli že lidé v domech nejen bydlí, ale také chodí kolem, přesto však udělali se svým názorem díru do světa. Podařilo se jim přesvědčit ostatní o své pravdě a nadlouho ovlivnili myšlení architektů a urbanistů na celém světě. Důsledky známe i u nás v podobě uniformních panelových sídlišť, které přímo vycházejí z jejich ideálů - i když s jejich provedením by asi Le Corbusier nesouhlasil. Konec dominance funkcionalistické architektury znamenal až nástup postmoderny v šedesátých letech a "less is more" nahradil Robert Venturi heslem "less is bore".



copyright tato grasso


A jak to souvisí s programováním? Každý, kdo někdy četl cizí kód, si hned všimne nezvyklých konstrukcí a způsobu formátování kódu. Někdy zaujme precizní tabulární formátování, někdy tvrdošíjná maďarská notace, jindy zas poznámky ve kterých vede programátor dialog sám se sebou. To vše odvádí pozornost čtenáře od funkce k formě. Nejsou to snad ornamenty?



Zdálo by se že tu ornamenty hájím, protože vnášejí krásu do všednosti, vítané potěšení znavenému duchu. Jenže mezi programováním a architekturou je jeden podstatný rozdíl. Architekt musí počítat s tím, že kolem jeho děl budou chodit i laici, kteří o zločinnosti ornamentu nemají nejmenšího tušení. Architekt by měl prostředí ušít na míru všem, nejen kolegům z branže a odborné kritice. Vhodně zvolený ornament pak může sloužit jako vodítko i (z architektova hlediska) nevzdělanému tupci, který by jinak jeho střízlivý obytný dům mohl považovat za vězení.



Kolem vašeho kódu ale laici chodit nebudou. Váš kód se dostane do ruky úzké skupině lidí s (někdy až příliš) specializovaným výcvikem, kteří budou mít za úkol se v něm zorientovat a provést potřebné změny. Pro ně je jakýkoliv ornament zdržením. Nejčitelnější kód je takový, který se striktně drží stanovených konvencí. Kód by měl vypadat tak, jako kdyby ho napsal jeden programátor během jednoho sezení.



Ornament v kódu je jakékoliv porušení stanovených konvencí. Napíšete-li kus kódu jinak, než je ve vašem týmu obvyklé, nutíte kolegy aby při čtení této části kódu přemýšleli ne o tom, co kód dělá, ale o jeho formátování - proč je zapsán tímto neobvyklým způsobem? Je za tím nějaká myšlenka, která mi uniká? Něco na co chtěl autor upozornit? Bohužel, většinou se jedná jen o neznalost konvencí, okamžitý nápad jak kód napsat "lépe" (v lepším případě), nebo o přesvědčení autora že jeho konvence jsou lepší a ostatní by je měli přijmout.



Jak mají vypadat konvence psaní kódu je sice sporná věc, ale jakmile se na nějakých shodnete, je nutné je dodržovat. Osobně preferuju držet se výchozích nastavení Visual Studia a ReSharperu. Proč pracně přenastavovat něco, co používají tisíce programátorů na celém světě? Tím odpadne mnoho diskusí ohledně formátování závorek a mezer kolem operátorů. Mnoho dalších věcí za vás nástroje nevyřeší (jmenné konvence, pořadí prvků atd.), ale na internetu je spoustu zdrojů a vzorových "code conventions" dokumentů i pro váš programovací jazyk.



Kód odpovídá strukturálním a technickým stránkám budovy. Je to nosný systém, vodotěsné zastřešení, rozvody energie a vody, výtahy. Pokud chcete postavit dobrý dům, je bezpodmínečně nutné udělat tyto základní věci dobře a umožnit údržbářům snadný přístup ke všem důležitým bodům. Na stavbě si nemůžete vymyslet svůj vlastní a zcela nový systém elektrických rozvodů a pojistek, nedělejte to ani v kódu. Na Grand Prix Obce architektů to sice nestačí, ale bez dobrého základu nepostavíte ani kůlnu. Dodržování konvencí zdaleka není všechno, ale usnadní život vašim kolegům (a také vám, až se budete za rok divit proč jste to tenkrát takhle napsali). Nechte si ornamenty do uživatelského rozhraní, ve stylistické úpravě kódu platí "bore is more".

*1 Le Corbusier ([1923] 1952): Towards a New Architecture. London
*2 http://cs.wikipedia.org/wiki/Funkcionalismus

UPDATED: 2008-07-26 Přidány citáty Mies van der Rohe a Roberta Venturiho.

Přednáška o CruiseControl.Net a MSBuild

| 1 comments |

Dneska jsem dělal interní přednášku pro kolegy ve firmě o průběžné integraci s pomocí CruiseControl.Net a MSBuild, a o našem build systému postaveném na těchto technologiích. PowerPointovou prezentaci si díky laskavému svolení mého zaměstnavatele můžete stáhnout zde.

Jediné co nám chybí k dokonalému build systému je Big Visible Cruise nebo The Orb. Ale až budu mít trochu času, chtěl bych nějakou takovou hračku pořídit;)

Jak udělat espresso

| 1 comments |

Protože mně Nikdo předal štafetu, tady je moje oblíbené video: Jak správně na espresso. Mimochodem, mají tam nádhernou mašinku. Jednou bych chtěl takovou doma:-)



Kafe které tam používají (Ethiopia Sidamo) se dá sehnat i u nás, např. já ho kupuju v Mama Coffee kde ho mají v super kvalitě a za rozumnou cenu.

NHibernate, lazy proxy třídy a chyba C# kompilátoru

| 0 comments |

Kompilátor C# v .NET verze 2.0 obsahuje chybu, díky které při použití where T : class pro omezení typového parametru generické třídy vygeneruje špatný IL kód. Díky tomu se takovou assembly vůbec nepodaří nahrát do paměti, runtime vyhodí výjimku:

System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)

Popis naleznete na Microsoftu nebo taky na Ayendeho blogu.

Když používáte NHibernate, a zvlášť pokud jste právě přešli na verzi 1.2, může se tato výjimka objevit i když vůbec where T : class nepoužijete. Jak to? NHibernate vám při načtení objektu může místo vaší třídy vrátit dynamicky generovanou proxy třídu (a ve verzi 1.2 je to default). V ní jsou všechny virtual members nahrazeny proxy metodou, která při prvním přístupu k objektu tento objekt načte z databáze (materializuje). No a když ve vašem objektu použijete virtuální metodu s generickým parametrem, např:

protected virtual T GetProperty<T>(string name)
{
...
}

snadno se dopracujete k výše zmíněné výjimce. Je to pravděpodobně (hlouběji jsem to nezkoumal) způsobeno tím, že generátor dynamické proxy třídy (Castle.DynamicProxy.dll) se pokouší emitovat kód, který podmínku where T : class obsahuje, a pokus nahrát tuto třídu do paměti skončí s chybou. Proto používáte-li NHibernate a "lazy" načítání objektů, nepoužívejte virtuální generické metody.

Jak může GridView přijít o ViewState

| 1 comments |

Aneb o side-effectech, CompositeDataBoundControl-ech a Watch okně.

Minulý týden jsem řešil zapeklitý problém: GridView občas při postbacku ztratilo řádky, tj. nenačetlo je správně z ViewState. Ovládací prvek, ve kterém bylo GridView použito, přitom na jiných webových stránkách fungoval bez problémů.

Nejdřív jsem pátral po vytváření ViewState a jeho ukládání do stránky a načítání při postbacku. Máme totiž poměrně složitou infrastrukturu odvozenou od třídy Page, která se nechová úplně standardně a občas vyvádí psí kusy. Jenže se to ukázalo jako špatná stopa: ViewState byl normálně uložen a přítomen ve skrytém poli v postbacku. Jeho struktura vypadala dobře, zdálo se, že jej prostě GridView ignoruje.

Trochu jsem se porejpal ve zdrojových kódech .NETu (God Bless The Reflector) a přišel jsem na to, že GridView má dceřinný ovládací prvek typu Table, který při postbacku dostane svůj ViewState a stará se o správnou rekonstrukci řádek tabulky. To znamená, že tento dceřinný prvek musí být vytvořen nejpozději v OnLoad. Jenže na mé stránce měl v události OnLoad můj GridView prázdnou kolekci Controls, ale přitom jeho ChildControlsCreated bylo true. Když neexistuje dceřinný prvek Table, nemá ani co nahrát ViewState.

Zajímavé přitom bylo, že pokud jsem ve Watch okně Visual Studia změnil ChildControlsCreated na false, okamžitě se přepsalo zpět na true a do kolekce Controls se přidal prvek typu Table! Chvíli jsem přemýšlel o woodoo a černé magii, pak jsem si ale prošel seznam ostatních položek ve Watch okně a začal jsem je zkoumat. Na první pohled nic podezdřelého neobsahovalo. Když jsem z něj ale odebral položku Controls.Count, toto podivné chování ustalo a ChildControlsCreated se klidně nechalo nastavit na false.

Vrátil jsem se proto s Reflectorem do kódu a našel jsem zajímavý side effect při přistupování na kolekci Controls, zavedený třídou CompositeDataBoundControl: Před vrácením kolekce se volá EnsureChildControls()! To vysvětluje ono podivné chování ve Watch okně, kde jsem měl kromě jiného i Controls.Count. Když jsem přepsal ChildControlsCreated na false, Visual Studio se pokusilo o refresh všech položek Watch okna, takže přístupem na kolekci Controls spustilo i metodu EnsureChildControls(), která nastavila ChildControlsCreated zpět na true.

A bylo jasno! Pokud přistoupím na kolekci Controls před natažením ViewState (tedy před OnLoad), způsobím volání metody EnsureChildControls(). Ta zavolá CreateChildControls(), ale pouze při prvním spuštění. No a CreateChildControls() nevytvoří žádný ovládací prvek, pokud nemá k disposici ViewState! Takže GridView nemá dceřinný prvek Table který načítá ViewState a ani jej nevytvoří, protože už je označená jako ChildControlsCreated=true.

Z toho, děti, plynou následující poučení:
  1. Používáte-li ViewState, nepřistupujte na kolekci Controls před událostí OnLoad. Platí to pro všechny potomky CompositeDataBoundControl, tedy GridView, FormView a DetailsView.

  2. Dávejte si pozor na to, co máte ve Watch okně, neboť to může mít (blahý i neblahý) efekt na běh kódu.


PS: Poslal jsem to jako bug na Microsoft, jsem zvědavej jak se s tím hoši poperou.
PPS: Nemusej to bejt jen hoši, koukal jsem nedávno na jeden rozhovor na Channel9 s Politou Paulus a má na stole (teda na okně:-) ocenění za návrh architektury DataBoundControls, takže možná je to její práce.

Jak ladit unit testy s ReSharperem

| 0 comments |

Tak jsem si nedávno do svého VS2005 nainstaloval nový zpomalovač ReSharper 2.0.1. Vypadá to moc chytře (někdy až moc - např. intellisense má VS lepší a daleko rychlejší), a v nové verzi je integrované spouštění unit testů. Když otevřete nějakou třídu s testama, vedle metod se objeví zelenožluté puntíky, kterými se dá jednoduše spustit nebo debuggovat unit test. Spouštění zafungovalo samo a bez problémů, jenže ladění ne a ne se rozběhnout. Pořád to místo unit test metody spouštělo celý projekt.

Pokud máte podobný problém, zkuste ho vyřešit takhle:

1. Pokud jste někdy laborovali s TestDriven.Net, zkontrolujte nastavení projektu. Otevřete Properties projektu obsahující test. Přejděte na záložku Debug. Ve skupině "Start Action" musí být vybráno "Start project" (někdy tam může být nastavený TestDriven).

2. V menu "ReSharper – Options…" zobrazte "Unit Testing" (úplně dole) a ve skupině "Debugging Method" vyberte "Use the project being tested". Výchozí hodnota je při instalaci nastavena na "Use the current startup project", což je, dle mého skromného názoru, volovina.

3. Pak už můžete spouštět testy kliknutím na zelenožlutý puntík vlevo od metody s atributem Test.

Také můžete sledovat průběh spouštění testů spolu s podrobným výstupem v okně Unit Test Runner. Pokud nevyskočí samo, otevřete ho z menu "ReSharper - Windows - Unit Test Runner". Takže nepotřebujete NUnit GUI a nemusíte si pořizovat TestDriven.

Jeden háček to ale má. Při každém testování R# změní v nastavení Solution výchozí startup projekt na právě testovaný projekt, takže pokud pak chcete normálně spustit aplikaci, musíte znovu nastavit výchozí projekt.

UPDATE: Pokud máte ReSharper verze 2.5 nebo nižší, nebudete moci debuggovat projekty umístěné v nějaké Solution Folder. Ve verzi 3.0 už to funguje.

Jak rozšířit XPath o vlastní funkce?

| 0 comments |

Že jazyky spojené s XML lze rozšiřovat, ví i můj jedenapůlroční synovec. Přidat si do XSLT šablony vlastní funkce je triviální prkotina a na netu existují tisíce návodů jak to udělat. Buď šoupnete funkci přímo do XSLT šablony do <ms:script> elementu, nebo přidáte objekt implementující kýžené funkce do XsltArgumentList objektu metodou AddExtensionObject (což je hezčí neboť to přímo nabádá k reuse). Takový přehled možností je zde. Jenže s XPath je to o něco komplikovanější.

Pokud voláte XPath dotazy nad nějakým XML dokumentem přímo z kódu, musíte na to jít úplně jinak. Zřejmě to není tak obvyklý scénář, protože jsem šťoural internet skrz naskrz a vůbec nebylo lehké přijít na nějakou stránku s návodem jak to udělat. Trvalo celkem dlouho než jsem objevil tohle: HOW TO: Implement and Use Custom Extension Functions When You Execute XPath Queries in Visual C# .NET.

Klíčový trik spočívá ve vytvoření vlastního XSLT kontextu, tedy třídy dědící od XsltContext. V ní předefinujete dvě metody: ResolveFunction a ResolveVariable. Když XPath parser narazí na něco, co vypadá jako funkce nebo proměnná kterou nezná, zavolá jednu z těchto metod a my máme šanci podstrčit mu vlastní implementaci. Řešení je to ale o něco složitější než u XSLT, protože musíte vrátit objekt implementující správné rozhraní - IXsltContextFunction nebo IXsltContextVariable. Ovšem s návodem už je to brnkačka, takže nemá cenu dál rozebírat detaily:-)

Jak zjistit celkovou velikost ViewState?

| 0 comments |

Když si zapnete podrobné trasování běhu stránky pomocí elementu <trace> v souboru web.config, ASP.NET vám do stránky vypíše strom ovládacích prvků a informaci o velikosti jejich ViewState a ControlState. Jaká je ale celková velikost dat odesílanýchve skrytém HTML poli __VIEWSTATE?

Na internetu lze nalézt několik návodů, jak přidat informaci o velikosti ViewState do trace logu. Jenže všechny co jsem našel zjišťují velikost podle Request["__VIEWSTATE"], ale to je navrácený ViewState, odeslaný z předchozí stránky! Pokud nás zajímá, kolik dat odesíláme ve ViewState aktuálně zpracovávané stránky, musíme použít daleko špinavější trik.

Výchozí PageStatePersister stránky pro ViewState je HiddenFieldPageStatePersister, který v metodě Save() zkomprimuje ViewState a ControlState a vloží jej do interního textového pole ClientState stránky. Stránka jej pak později narenderuje do jednoho nebo více skrytých polí __VIEWSTATE (záleží na nastavení vlastnosti stránky MaxPageStateFieldLength). No a tak si zjistíme jak je toto pole dlouhé a přidáme to do trace logu:

protected override void Render(HtmlTextWriter writer)
{
#if DEBUG
System.Reflection.PropertyInfo secretState =
this.GetType().GetProperty(
"ClientState",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic
);
string viewState = (string)secretState.GetValue(this, null);
if (viewState != null)
Trace.Warn("ViewState Size", viewState.Length.ToString());
else
Trace.Warn("ViewState Size", "0");
#endif
base.Render(writer);
}

No a je to. Informace o velikosti ViewState je vidět přímo v trace logu stránky! Dobrý, ne? Reflexe interního pole je ovšem špinavá technika a jsou k ní potřeba určitá práva. Pro ladění stránek a velikosti ViewState je to však príma.