Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Lepší události v C#

14.11 - 10. května 2011 | ASP.NET 2.0

Když se nedávno ptali Anderse Hejlsberga na to, co by nejradši v C# změnil nebo udělal jinak, kdyby mohl, odpověděl, že by zrušil události. Přesněji, že by je spojil s konceptem vlastností. Na první pohled jsou totiž vlastnosti a události celkem podobné, ale na druhý zjistíte, že události mají poměrně složitou syntaxi a vyžadují psát ne moc pěkný kód.

Dřív (C# 1.0) to asi o moc líp udělat nešlo, ale dnes tomuto přání můžeme plně vyhovět. Stačí události vystavovat jako vlastnosti typu IObservable<T>. Toto rozhraní je jednou z novinek BCL .net 4.0 a hlavně reaktivních extenzí (Rx.net).

Pokud píšete nový kód, zvažte užití IObservable<T> namísto událostí. Např. F# má události takto řešené nativně.

Ze starého na nový

Vezměme si ukázkovou deklaraci události v C#:

Dříve jsme museli definovat vlastní typ delegáta události:

public delegate void ChangedEventHandler(object sender, EventArgs e);

Pak vystavit událost tohoto typu:

public event ChangedEventHandler Changed;

A nakonec někde v kódu událost odpálit:

if (Changed != null)
  Changed(this, e);

Nehledě na podivné přetížení operátoru pro zavěšení ovladače události:

List.Changed += new ChangedEventHandler(ListChanged);

Nové verze jazyka a BCL nám přinesly mnohá usnadnění od EventHandler<TEventArgs>, přes automatického odvození typu delegáta až po lambda výrazy. To však nic nezměnilo na samotné koncepci událostí…

A teď si to tedy přepišme na sjednocený model vlastností a událostí s užitím IObservable<T>:

readonly Subject<Unit> changed = new Subject<Unit>();

public IObservable<Unit> Changed {
  get { return changed; }
}

V kódu událost vyvoláme následovně:

changed.OnNext(new Unit());

A ke zpracování události se přihlásíme takhle:

List.Changed.Subscribe(ListChanged);

Krom čistšího kódu (všimněte si absence defenzivního kódu a přetěžování operátorů, které na první pohled nemusí být každému jasné, co dělá) máme i spoustu dalších výhod, které přináší reaktivní programování.

PS. Třidy Subject<T> a Unit nejsou součástí BCL, ale zmiňovaných Rx.net. Do projektu si je přidáte pomocí nuget příkazu Install-Package Rx-Main.

Nepište zbytečný kód

07.19 - 3. srpna 2009 | ASP.NET 2.0

Jedním ze základních pravidel psaní přehledného kódu je nepsat zbytečný kód. Pojďme si ukázat pár příkladů zbytečného kódu, kterému je zahodno se raději vyhnout.

Komentáře

Programátoři jsou často velice kreativní lidé, ale svou kreativitu si vybíjí nesprávným způsobem. Tak se může stát, že během své kariéry narazíte na skvosty typu:

  1. zakomentované kusy kódu
  2. označení dočasného kódu v kometáři
  3. vylévání srdíčka nad vlastní blbostí

K bodu prvnímu. Když něco takového potkáte, rovnou to smažte, nesnažte se nad tím přemýšlet. Jestli někdy ten kód budete potřebovat, najdete ho ve vašem VCS. Pokud se přistihnete, že máte označený blok kódu a chystáte se zmáčknout magickou kombinaci kláves Ctrl+K, C, ušetřete si práci a rovnou zmáčkněte Delete. ;)

K bodu druhému. Nejprve zjistěte, kdo to napsal a dejtemu rovnou pohlavek nebo seberte prémie. Nejlépe oboje, u někoho lépe fungují podmíněné reflexy spojené s fyzickou bolestí, někdo si vzpomene, že mu nezbyly peníze na tu skvělou chlastačku a už to nebude chtít opakovat. Zjišťovat, co na koho platí je moc drahé. ;)

Pro představu, našli jsme takovýto kód:

//Testovaci verze
if (Request.Cookies.AllKeys.Contains("ticket")) {
  InvitedUser iuser = _invitedUsersRepository.FindByHash(Request.Cookies["ticket"].Value);
  if (iuser != null) {
    iuser.Valid = false;
    _invitedUsersRepository.Save(iuser);
  }
}
// --
;

Autora jsme ztrestali. První krok máme za sebou, co dál? Tohle asi hned smazat nepůjde. Prvním možným krokem, je nadefinovat build symbol a tyto bloky obalit preprocesorovou podmínkou:

#if TEST_VERSION
if (Request.Cookies.AllKeys.Contains("ticket")) {
  InvitedUser iuser = _invitedUsersRepository.FindByHash(Request.Cookies["ticket"].Value);
  if (iuser != null) {
    iuser.Valid = false;
    _invitedUsersRepository.Save(iuser);
  }
}
#endif
;

Sice jsme se nezbavili špatného kódu, ale aspoň pujde dočasná featura vypnout z jednoho místa a kód hovoří trochu jasněji.

Pokud napíšete blok kódu a pocítíte potřebu uvodit ho komentářem, nebo takových bloků máte zasebou víc, je čas refaktorovat! Místo komentářů vytvořte metody s popisnými názvy (ekvivalent komentáře). Komentáře mají tu vlastnost, že rychle ztrácejí význam a zůstávají v místech, kde už není co komentovat.

Pravidlo: Když ucítíte potřebu napsat v kódu komentář, zamyslete se, zda nenajdete lepší způsob jak tuto myšlenku vyjádřit.

Poznejte svůj jazyk

Za programátora se považuje každý, kdo se v nějakém jazyce naučí pár příkazů. Někdo si vystačí s málem a pak mu nezbývá než prasit. Základem je poznat jazyk, ve kterém se chystáme sdělovat své myšlenkové pochody. Zdrojový kód se mnohem častěji čte než píše, proto by měl obsahovat co nejméně zbytečností, které odvádějí pozornost.

Začněme třeba u JavaScriptu. Často se setkávám s kódem, který napovídá, že autor má hrubé znalosti C a jemu podobných jazyků, ale o JavaScriptu moc potuchy nemá.

Podmíněné přiřazení

Jednou z typických ukázek může být validace vstupních parametrů a nastavení výchozí hodnoty, pokud přišlo null. Nejhorší případ:

function example(options) {
  if (options == null) {
    this.options = { color: 'red' };
  }
  else {
    this.options = options;
  }
}

O něco chytřejší programátor použije ternární podmíněný operátor:

function example(options) {
  this.options = (options != null) ? options : { color: 'red' };
}

Vývojář znalý JavaScriptu však odbourá zbytečné ruchy a vznikne logický zápis:

function example(options) {
  this.options = options || { color: 'red' };
}

Pojďme teď dál k mému oblíbenému C#. Předchozí příklad tam nejde řešit operátorem || protože C# je silně typový a null != false. Ovšem C# 2.0 přinesl s Nullable<T> hodnotovými typy i operátor ??. Ten slouží jako zkrácená varianta ternárního podmíněného operátoru. Díky němu můžeme následující kód:

public void Example(Options options) {
  _options = (options != null) ? options : new Options { Color = Color.Red };
}

Pěkně odšumět:

public void Example(Options options) {
  _options = options ?? new Options { Color = Color.Red };
}

Pokud pracujete s Nullable hodnotovými typy, zkraťte i následující zá­pis:

public void Example(int? pageIndex) {
  int page = 1 + (pageIndex.HasValue ? pageIndex.Value : 0);
}

Na:

public void Example(int? pageIndex) {
  int page = 1 + (pageIndex ?? 0);
}

Generické parametry

Dalším, velice častým šumem, je explicitní uvádění generických parametrů, tam kde to není potřeba. Za příklad si vezměme třeba generickou metodu Assert.Equal<T>(T expected, T actual) z frameworku xUnit.net. Můžeme ji volat takto:

Assert.Equal<string>("test", null);

Jenže kompilátor není žádný hlupák a dokáže správně dosadit typ generického parametru podle typu předávaného parametru. Proto je možné napsat, mnohem přehlednější zápis:

Assert.Equal("test", null);

FxCop vám přímo radí: Pište generické metody tak, aby se typ generického parametru dal implicitně odvodit.

Zbytečné volání ToString

Často se také můžete setkat s voláním metody ToString tam, kde to není pořeba. Příklad:

double rating = 3.8;
string output = "Your rating is: " + rating.ToString();

Tohle lze napsat bez šumu:

double rating = 3.8;
string output = "Your rating is: " + rating;

Teď si jistě, říkáte, že jsem asi blbec, jak můžu po silně typovém C# chtít, aby sečetl string a double. Nuže můžu. Krom toho, že je C# silně typový, podporuje i přetěžování operátorů! A designéři třídy String přidali přetížení operátoru + na kterýkoliv Object, na kterém zavolají metodu ToString za vás. :)

Zbytečné explicitní přetypování

Občas se bohužel můžeme v kódu potkat s věcmi, nad kterými zůstává rozum stát. Někdo například už pochopil dědičnost. Pochopil, že třída List<T> implementuje rozhraní IList<T> a to dědí z ICollection<T>, které je potomkem obecného rozhranní IEnumerable<T>. Jenže pak přijde na praktické použití a je zle:

public class UserDetailViewData {
  public IEnumerable<User> Friends { get; set; }
}

public class UsersController : Controller {
  public ActionResult Detail(userName) {
    IList<User> friends = GetFriendsOfUser(userName);
    return View(new UserDetailViewData {
      Friends = (IEnumerable<User>)friends
    });
  }
}

Takhle vytržené z kontextu to nemusí vypadat až tak zle, ale originál mě stál málem ukroucení hlavy.

Roztahaná inicializace

Novinkou C# 3.0 jsou inicializátory. Zvažte následující kód:

var user = new User();
user.Name = "Josef Novák";
user.Gender = Gender.Male;

Přepsat na:

var user = new User {
  Name = "Josef Novák",
  Gender = Gender.Male,
};

Vyhnete se tím, zbytečnému opakování user..

Závorky navíc

Častý šumem v kódy bývají také závorky, které nemají žádný význam, zejména u inicilizátorů s bezparametrickým konstrukotrem new User() { Name = "Josef Novák" } a u anonymních bezparametrických delegátů delegate() { return true; }. Tyto závorky klidně smažte a u delegátů zvažte kompresi na lambda výraz: ()=> true.

Na druhou stranu existuje spousta případů, kdy vhodně vložené závorky zvyšují čitelnost.

Závěr

Když říkám, že je lepší psát némě kódu, rozhodně tím nemám na mysli kriptické zápisy alá regexy. To, co ušetříte na zbytečném kódu, klidně investujte do popisných názvů proměnných a metod. Důležitá je čitelnost. Se spoustou výše zmiňovaných úprav vám mohou pomoci nástroje, např. CodeRush Express, který je zcela zdarma.

Rozhodně jsem nepokryl všechny možnosti, kde psát zbytečný kód navíc. Jistě taky některé znáte, podělte se o ně v komentářích. :)

Vracet List je špatné

08.51 - 1. srpna 2009 | ASP.NET 2.0

Přinejmenším na veřejnosti. Mnohdy se v různých APIs můžete setkat s tím, že různé objekty veřejně vystavují metody/vlasnosti, které mají návratový typ List<T>, v lepším případě IList<T>. Krásná ukázka nerozvážného návrhu.

Proč je špatné vracet List<T>

Když vracíte List<T>, znamená to, že říkáte všechno o vnitřní implementaci a zavíráte si vrátka pro její možnou změnu. Generický List sice neni nerozšiřitelný (sealed), ale ani nebyl při návrhu moc k rozšiřování zamýšlen. Navíc tim porušujute jeden ze základních principů OOP – a to zapouzdření. Dále tim umožňujete konzumentovi vaši kolekci přímo měnit.

Ukázka naivního/lenošného přístupu

Pro ukázku mějme třídu reprezentující uživatele User, kterému můžeme přiřadit uživatelské role Role. Naivní přístup:

public class User {
  public List<Role> Roles { get; private set; }
}

public class UserConsumerCode {
  public void SomeMethod {
    var user = new User();
    user.Roles.Add(Role.Admin);

    // ...

    user.Roles.Remove(Role.Reviewer);

    // ...

    var isAdmin = user.Roles.Contains(Role.Admin);
  }
}

Jak vidíte, klient má nad kolekcí rolí uživatele plnou moc. Může si přidávat, odebírat, přetřizovat jak se mu zlíbí. A my nemáme nejmenší šanci tyto celkem důležité operace jakkoli ovlivnit, nebo u nich vědět. Kdybychom změnili typ kolekce Roles na IList<Role>, tak sice můžeme změnit vnitřní implementaci a místo List<T> použít třeba LinkedList<T>, ale stále zveřejňujeme příliš mnoho. Jedinou výhodou tohoto řešení je, že jsme nemuseli napsat moc kódu a máme hodně funkcionality.

Jak zlepšit náš kód?

Rovnou přeskočím použití ICollection<T>, a přejdeme k tomu, jak by to mělo vypadat. Zachováme si zapouzdření, uzavřeme se vůči nechtěné manipulaci z vnějšku a otevřeme se snadným vnitřním změnám a rozšiřitelnosti (Open/closed principle).

public class User {
  private readonly ICollection<Role> _roles;

  public User() {
    _roles = new List<Role>();
  }

  public IEnumerable<Role> Roles {
    get { return _roles.ToArray(); }
  }

  public void AddRole(Role role) {
    _roles.Add(role);
  }

  public void RemoveRole(Role role) {
    _role.Remove(role);
  }

  public bool IsInRole(Role role) {
    return _roles.Contains(role);
  }
}

Pěkně jsme obohatili a zpřehlednili naše API (zápis user.IsInRole(Role.Admin) říká mnohem více, než user.Roles.Contains(Role.Admin), navíc neporušuje Demeterův zákon), skryli jsme implementační detaily a zároveň nechali otevřená vrátka pro snadnou výměnu vnitřní implementace… Dalším krokem by měly být kontrakty manipulačních metod, aby byly bezpečné. Dále pak můžeme přidat vyvolávání událostí, abychom o manipulaci věděli. Míst pro rozšíření je tu spousta.

Hloupá otázka na závěr

„Ale, jak teď zjistim, kolik rolí má uživatel přiřazených, když IEnumerable nemá vlastnost Count ani Length?“ using System.Linq; ;)

Odpojte se k nám!

08.29 - 2. prosince 2008 | Webdesign

Sháníme posily do našeho týmu. Pokud znáš dobře dotnet nebo Javu a chceš se učit novým věcem, tak čti dál.

Představení

Jsme Twareg (čteme tuareg, ale někdy i tvarek), firma zabývající se vývojem webových aplikací, které jsou výkonné, dostupné a škálovatelné. K tomu primárně užíváme platformu Microsoft .NET (C#, ASP.NET). Používáme nejmodernější technologie (např. ASP.NET MVC, Dynamic Data, WCF, „Velocity“) a snažíme se o moderní postupy při vývoji našeho software. Všichni máme zkušenosti z portálu Atlas.cz a jsme ochotní se o ně podělit. Když řeknu, že jsme dynamický tým, asi bych lhal. Představte si dynamický tým a svou představu vynásobte deseti. :)

Naše kancelář je vybavena různobarevnými pěnovými míčky, a když vás někdo naštve, můžete si po něm hodit. Dostáváme stravenky a chodíme je společně projíst někam na Anděla. Věhlasný je také náš rituál vaření, který jsme si přinesli z Atlasu.

Pokud hledáte naše webové stránky, tak vězte, že jsme si žádné ještě nestihli udělat, ale máme to v plánu. najdete je na adrese www.twareg.com. Pokud vás zajímá naše zázemí, tak vězte, že jsme spřátelenou společností Ataxa a sídlíme ve stejné budově.

Na čem děláme

Našim hlavním produktem je codename „Hyas“. Je to reklamní systém, který si klade za cíl poučit se z chyb konkurenčních systémů, zachovat si jejich výhody a přinést něco nového. Být prostě lepší. Jak prosté. Nyní máme téměř připravené jádro systému, které půjde brzo do testování. Potřebujeme ale další lidi, kteří by nám pomohli nad ním napsat veřejné API (SOAP, REST), administrační/u­živatelské rozhranní a další potřebné komponenty. Pokud vás toto zajímá, neváhejte mě kontaktovat!

Pokud byste raději dělali na webech, je tady ta možnost také. Děláme na Web 2.0 projektech založených na znovupoužitelných komponentách (a tím rozhodně nemyslím jen server controly). Aktuálně děláme katalog nightlife podniků se silnou integrací sociálních prvků. A ve frontě čekaj další neméně zajímavé. Pokud se na nich chcete podílet, ozvěte se mi!

Koho hledáme

Předně schopné vývojáře. Takové, co mají chuť se učit novým věcem a chtějí se o své poznatky dělit. :) Takové, co mají týmového ducha, kterým nevadí zůstat občas v práci dýl a zahrát si třeba Wiičko nebo jít společně do kina, na bowling nebo jenom na jedno.

Proč jsou statické helpery zlo?

10.53 - 22. listopadu 2008 | ASP.NET 2.0

Minulý týden psal Arthur spotík o takovém tom víkendovém programování, ve kterém šlo hlavně o to, že přešel na Vistu, vytvořil si gadget pro přehrávání ČRo a taky editor hosts souboru. Napsal ho v C# a zmínil se o tom, že použil techniku, se kterou nejsem s to…

Zkrátka podědil si generickou Dictionary<TKey, TValue> a rozšířil ji o serializaci. Dictionary není serializovatelná. Je to dáno jak její generickou povahou (constraints pro atributy neexistují), tak tím, že sídlí v knihovně, která nesmí mít závislost na jiné, která serializaci zajišťuje.

public class SerializableDictionary<TKey, TValue>
  : Dictionary<TKey, TValue>, ISerializable {
  // ...
}

To byla moje chvíle, vyčmuchal jsem zde codesmell (zbytečné dědění, které by šlo vyřešit kompozicí). Neomaleně jsem vstoupil do diskuse chválící gadget a sprostě Arthura pohanil. Omlouvám se za to. Mým úmyslem bylo ho postrčit o kus dál, možná rozšířit obzory. Prostě sprostá rada, která by mi v začátcích se C# pomohla nebloudit labyrintem macatých tříd se spoustou dovedností a košatou hierarchií předků a potomků.

Alternativní řešení

V diskusi, kterou jsem tímto vyvolal, padlo i to, že jde o porušení Single Responsibility Principle, které už rozebral Borek. Arthur tedy nabídl, alternativní řešení, jak se s problémem vypořádat:

Samozřejmě jsem si mohl napsat i kašpárkovskou třídu, co má dvě statické metody (Serializuj a Deserializuj) a veřejný parametr „Dictionary“… šlo by to vyřešit mnoha způsoby, lišícími se co do pracnosti, elegance a efektivity, a já se rozhodl nedělat „composition blackbox“, ale podědit třídu – především proto, že bych musel zpřístupňovat spoustu veřejných metod od původní generiky, nebo deklarovat vložený objekt jako veřejný.

public static class DictionarySerializer<TKey, TValue> {
  public static byte[] Serialize(Dictionary<TKey, TValue> dictionary) {
    // ...
  }
  public static Dictionary<TKey, TValue> Deserialize(byte[] data) {
    // ...
  }
}

Přejdu teď composition blackbox – vrátím se snad k němu někdy jindy, ale zastavím se u statických metod. Myslím si totiž, že to je další cesta do pekel… :)

Staticky vs. Instančně

Na toto téma se vedlo jedno z vláken té jinak pěkné diskuse o šikovném gadgetu. (Platíte koncesionářské poplatky?) Osobně zastávám názor, že je lepší se statickým helperům vyhýbat. Jednak ze zkušenosti a druhak z některých signálů a pouček správného OO návrhu.

V objektových jazycích odvozených od C jsme si zvykli používat třídy. Třída je předpis, podle kterého se vyrábějí objekty. Objekty jsou přenašeči dat nebo službami. Mohou být tedy obdařeny rozhraním, přes které s ním komunikují jiné objekty. Jenže ve světě Céčkových jazyků, můžeme mít tzv. statické členy. Statický člen existuje jen jednou, po celou dobu života aplikace.

Jenže, co je to život aplikace? Nevím jak je to přesně v PHP, ale myslím, že tam je to cca jeden požadavek. Statické proměnné tu slouží jako šikovná náhrada globálních proměnných a statické metody dovolují volat akce bez nudného vytváření instance třídy. To je jistě super. Ve světě dotnetu je to však mnohem složitější. V ASP.NET žije aplikace dokud ji někdo nevypne, tj. neresetuje aplikační pool, nezmění konfiguraci nebo neaktualizuje binárky. Takže u statického členu nikdy netušíte, jak dlouho bude žít.

Dalším problémem je konkurence. V PHP se aplikace spouští při každém požadavku a po jeho skončení opět vyhnije a to vše se děje v rámci jednoho vlákna. V ASP.NET žije aplikace mnoho požadavků, které jsou zpracovány samostatnými vlákny. Vzhledem k tomu, že statické členy jsou v aplikaci jen jednou a to pro více vláken, musíte najednou začít řešit konkurenční přístup, k těmto členům.

Další z problémů statických členů je ten, že třída není objekt, tudíž by se s členy třídy nemělo počítat v objektovém návrhu. :) Statické metody ani vlastnosti nelze popsat rozhraním, při jejich užití vzniká úzká vazba. Takovéto členy se také krásně vyhýbají dědičnosti. Nelze je přepsat tradičními postupy.

Takhle bych ve zkratce shrnul hlavní nevýhody statického přístupu. Platí jen pro určité případy. Nemusíte se s nimi nutně setkat a pro váš konkrétní případ mohou být i výhodné. Pokud však navrhujete API, které má být znovupoužitelné (hlavní výhoda OOP), nikdy nevíte, v jakém prostředí poběží. A když už píšeme objektově, tak proč rovnou nepsat tak, aby naše třídy znovupoužitel­né byly?

Jak tedy s těmi helpery?

Jak říká Michal Bláha, „statický helpery jsou výbornej syntax sugar.“ Jasně, proč ne? Ve skriptech může být statický helper jasným přínosem. Je ale třeba myslet na některá pravidla.

Základním pravidlem, pokud chci vytvořit statický helper, je vytvořit instanční implementaci. Dobrou ukázkou tohoto přístupu je například System.IO.File, který je statickou fasádou nad System.IO.FileInfo. Podobně funguje např. i metoda Rarous.TexyNet.Texy.Process, která slouží k rychlému převodu textu do HTML, ale je tu i silné instanční API, které tato metoda volá.

Závěr

Statické helpery jsou zlo, když jsou primárním návrhem a taky jediným. Pokud dosahujete kompozice objektů pomocí dependency injection, jsou pro vás statické helpery nedosažitelné (bez napsání instančního wraperu). Když píšete unit testy, nemůžete snadno statický helper nahradit. Pokud máte statické vlastnosti, musíte myslet na konkurenční přístup a životnost zdrojů, které takto zapouzdřujete.

public interface IDictionarySerializer<TKey, TValue, TData> {
  TData Serialize(IDictionary<TKey, TValue> dictionary);
  IDictionary<TKey, TValue> Deserialize(TData data);
}

Přece jen existuje více cest k cíli a je možné, že se někde šeredně pletu. Jaký je váš pohled na věc?

Vizualizace vlastních typů v debuggeru

07.54 - 16. ledna 2008 | ASP.NET 2.0

Jistě všichni znáte možnost jak přepsat výchozí zobrazování vlastního typu v debuggeru VS pomocí přepsání metody ToString(). Ovšem někdy je tato metoda velice užitečná i na jiné účely, co potom s tím?

Když debuguje ve VS vlastní typy ve watches se vám u proměnné zobrazuje celý název typu, např. Rarous.NForms.TextItem. Pokud chcete v debuggeru vidět užitečnější informace, zpravidla přepíšete metodu ToString(), kterou má každý objekt, tak, aby vypisovala to, co chcete. Např.:

public override string ToString() {
  return "\"" + Control.Name + "\", Value=" + Control.GetValue();
}

Při debutování by se nám pak mohlo zobrazovat něco jako "email", Value=frantisek.koudelka@sts-chvojkovice.cz. Jenže. Teď chci, aby se mi v šabloně při zápisu <%=Form["email"]%> vypsal kód formulářového prvku a jeho labelu. Jak to udělat, aby se zachovalo pěkné debugování a přibyla nová a ještě lepší funkčnost?

Naštěstí existuje jiné řešení, jak debugovací informace zobrazovat. Troufnu si tvrdit, že i mnohem čistší. V .net frameworku existuje atribut System.Diagnostics.DebuggerDisplayAttribute, který je určen, právě k zobrazování informací v debuggeru. Jeho užití je následující:

[DebuggerDisplay("{Control.Name}, Value={Control.GetValue(),nq}")]
public class TextItem : FormItem {
  // implementace třídy...

  public override string ToString() {
    return Label + " " + Control;
  }
}

Tímto jsme dosáhli, nové funkčnosti metody ToString() a zachovali jsme vlastní informace zobrazované v debuggeru. Více informaci o atributu DebuggerDisplay, najdete v MSDN.

Tagy: C#, debugger, NForms

Chyba v kompilátoru C# 3.0

11.28 - 27. listopadu 2007 | ASP.NET 2.0

Po prvních dnech s novým Visual Studiem 2008 jsem narazil na menší problém v kompilátoru C# 3.0. Když jsem si v práci otevřel náš Opus, nešel zkompilovat, ačkoli v 2005 je vše ok. Problém byl v tom, že nový kompilátor má problémy rozpoznat plné nesystémové namespacy a tak např. zápis Atlas.Web... chápe jako Atlas.Opus.Atlas.Web.... Takový namespace neexistuje a pak nejde kód zkompilovat.

Ukázka zkráceného kódu, kde může nastat chyba:

namespace Atlas.Opus.Web {
  public class SomeClass {
    // nejaky kod tridy
    var value = Atlas.Web.Utils.Config.GetInt32("key"); // tady
    // dalsi kod tridy
  }
}

Řešení se nabízejí přinejmenším dvě.

  1. Použít globální identifikátor např. global::Atlas.Web..., čímž se problém vyřeší, ale zápis je ještě o kousek delší, než by musel být.
  2. Tam, kde to jde, použít using namespace.
  3. Tam, kde hrozí kolize názvů tříd, použít aliasy pomocí klíčového slova using např. using utils = Atlas.Web.Utils; a v kódu pak upravit volání na utils::Config....

Je to docela zajímavá vlastnost, že nová verze kompilátoru interpretuje namespacy jinak než předchozí verze. Naneštěstí je to vlastnost nepěkná a přináší v některých případech práci navíc.

Podivný operátor /*!*/

15.58 - 28. července 2007 | ASP.NET 2.0

Četl jsem si zdrojáky IronRuby a v deklarcích metod se často objevovalo podivné /*!*/. Např.

public RubyConstantAttribute(string/*!*/ name) {
  if (name == null) throw new ArgumentNullException("name");
  _name = name;
}

Nejpve jsem moc nechápal, asi nějaká interní věc (coding style) pro označování něčeho. Kontrakt v ukázce možná napoví, že by parametr neměl být null. Ale počkat, něco takového má přeci Spec#, NonNullable types. Znáte to ne, v C# 2.0 máme Nullable Value types, vyznačují se tím, že za jejich názvem je otazník, např. int?. No a Spec# ještě přidává NonNullable Reference types s vykřičníkem na konci, např. string!. No jo, ale IronRuby je přece napsaná v C#…

Kompilátor Spec# má tu výhodu, že dokáže zkompilovat C# kód. A dokonce pokud se Spec# kód mírně poupraví, tak jde naopak snadno zkompilovat kompilátorem C#. Nějak se nám to zamotává :). Pokud chcete Spec#ový kód zkompilovat v C#, ale nechat ho zkompilovatelný ve Spec#, musíte použít speciální komentáře //^ pro jednu řádku, /*^ ^*/ pro blok. A ještě je tu jedna výjimka a to právě u toho vykřičníku. /*!*/ je zkrácenou verzí /*^!^*/. A jsme doma…

Snad se dočkáme Spec#ových vychytávek v nějaké přístí verzi C#, třeba v té čtvrté. Protože předchozí ukázka by mohla vypada odost uhlazeněji:

public RubyConstantAttribute(string! name) {
  _name = name;
}

a výsledek je stejný.

Blok using

19.33 - 28. července 2006 | Tuning

Jazyk C# je obdařen velice zajímavým blokem uvozený slovem using. Víte k čemu slouží? Používáte ho? Pokud jste si na jednu z předchozích otázek odpověděli negativně, měli byste si přečíst následující řádky. :)

Klíčové slovo using má v C# hned dva významy. Tím prvním je import namespaců a vytváření aliasů k třídám, ale ten je pravděpodobně provařený. Druhým významem je uvození bloku, ve kterém se používá komponenta implementující rozhranní IDisposable. Implementace IDisposable se používá k uvolňování zdrojů.

SqlConnection conn  = new SqlConnection(connString);
conn.Open();
// práce s databází
conn.Close();

To byla ukázka naivního přístupu k databázi, kdy se spoléháme, na to, že vše proběhne v pořádku a dojde k zavolání conn.Close(), což může být klidně pravda, ale pokud dojde k vyvolání jakékoli vyjímky před zavoláním conn.Close(), zůstane připojení stále otevřené, dokud nedojde k timeoutu a objekt conn nezpracuje GC. To může trvat hodně dlouho a zabírat tak zbytečně cenné systémové zdroje.

using (SqlConnection conn  = new SqlConnection(connString)) {
    conn.Open();
    // práce s databází
}

A takle by se to mělo psát. :) Blok using přijímá jako parametr disposovatelné komponenty a na konci bloku zavolá IDisposable.Dispose(). V případě SqlConnection volá metoda Dispose metodu Close. Nutno podotknout, že k zavolání metody Dispose dojde i při vyhození vyjímky. Jak je toho dosaženo? Blok using neni nic jiného než zkráceným (a pro mě i přehlednějším) zápisem následujícího kódu.

SqlConnection conn  = new SqlConnection(connString);
try {
    conn.Open();
    // práce s databází
}
finally {
    conn.Dispose();
}

Toť celé tajemství a kouzlo bloku using. Závěrem bych vám chtěl ještě doporučit blok using používat všude tam, kde používáte nejrůznější Readery, Writer a jiné třídy implemetující rozhranní IDisposable, protože tak můžete ušetřit cenné zdroje a zvýšit tak výkon vaší aplikace :) Hodně štěstí…

Evoluce ve webovém vývoji

07.57 - 23. května 2006 | ASP.NET 2.0

Možná jste si všimli, že se v poslední době pouští Microsoft do AJAXu. Jejich Atlas framework je zatím v alfa stádiu, ale i tak je plně použitelný a funkční – až na pár bugů. Tímto frameworkem otevírá dveře vývojářům serverside aplikací (typicky píšících v C#, Javě, VB.NET nebo i v PHP) ke vstupu na klienta. Atlas se snaží přinést do JavaScriptu i jakousi podporu OOP, takže můžete používat namespacy, classy, interfejsy (fuj to je hnusný slovo – zdravím doc. Müllera), dědičnost a další vymoženosti, na které jsem zvyklý ze C#.

Problém je ovšem v tom, že pořád musím psát v JavaScriptu. Nemám rád tenhle jazyk. Občas v něm něco napíšu, ale je to peklo – právě proto že v něm málo píšu, ho tak dobře neumím, a protože ho tak dobře neumím, tak v něm málo píšu. :) Atlas naštěstí přináší i deklarativní metodu psaní aplikací pomocí XML-Scriptu, který je mi podstatně bližší. Ale stejně v něm nejde napsat vše – hlavně vlastní komponenty.

Protože bude Atlas součástí příští verze Visual Studia „Orcas“, přemýšlejí soudruzi v Microsoftu, jakým způsobem zjednodušit vývoj aplikací v Atlasu, připravit kvalitní vývojové nástroje se všemi vymoženostmi (Intellisense, debugging apod.). Vzhledem k tomu, že je JavaScript dynamicky typovaný, je s Intellisence celkem problém – dneska se ve studiu objeví jen zřídka. To by ale nebyl Microsoft, kdyby nepřišel s něčím novým…

Na scénu přichází Script#

Ano, je tu to, na co jsem dlouho čekal, konečně může psát klientské skripty v pořádném jazyku. O co tedy jde? O zimních prázdninách si Nikhil Kothari – duchovní otec Atlas frameworku – uvědomil, že se v JS píše fakt blbě a že by bylo lepší psát v C# i skripty. Jenže jak donutit prohlížeče, aby zpracovali C#? To je prakticky nemožné. Nikhil na to šel od lesa a napsal vlastní kompilátor, který místo MSIL generuje JS. Přidal k tomu ještě sscorelib a máme tu Script# (skriptšárp). Script# je fakticky C#, nemusíte se učit žádný nový jazyk (teda pokud umíte C# jako já). Navíc dostanete v balení knihovnu, která obsahuje standardní DOM a vůbec vše, co můžete najít v JavaSriptu. Kompilátor generuje JS kód určený pro Atlas framework, takže výsledkem může být jak js soubor, tak dll, kde jsou skripty jako resource – stejně jako je tomu u poslední verze Atlasu. Kompilátor zvládá DEBUG – včetně preprocesoru – i minimalizovat kód (a to jak ořezání bílých znaků, tak i náhradu jmen proměnných za zkratky jako $0). Je toho moc, nejlépe, když si to vyzkoušíte sami nebo se podíváte na desetiminutovou video ukázku. Zatím je kompilátor ve fázi funkčního prototypu a k dokonalosti potřebuje ještě hodně práce, ale jako ukázka možného směru je to bomba.

Zajímavost závěrem

Docela zajímavé je, že soudruzi v Google přemýšlejí podobně a podstatě totéž přinášejí ve svém Web Toolkitu.

Zdroj

Nikhil Kothari – Script# Prototype