C# (2) funkcie na ktoré som skutočne pyšný


Každý kóder určite vymyslel nejaký kód, na ktorý je pyšný. Ja som pyšný na 2 funkcie, ktoré používam v našich projektoch denno-denne. Niekomu sa budú zdať smiešne, no pre mňa sú jedinečné.

 


Uvádzam len maličkú deklaráciu:
public static T CacheWrite<T>(string key, T value, DateTime expire)
{
    // ...
}

public static T CacheRead<T>(string key, Func<string, T> onEmpty = null)
{
    // ...
}
    
A excelentné použitie:
var obj = CacheRead<MyClass>("myclass.key", key =>
{
    var obj = new MyClass();
    return CacheWrite(key, obj, DateTime.Now.AddMinutes(2));
});
    

Do našich projektov som potreboval niečo jednoduché (key,value,expire), čo by mi pomohlo ľahko cacheovať a hocikedy zmeniť cache providera. Podarilo sa. Som pyšný, že som to vymyslel a neskopíroval (vylepšené) z internetu.

 
Peter Širka
pred 4 mesiacmi, 20.01.2012

Diskusia (14)

1
Inac prave nedavno bola diskusia okolo UtcNow vs Now. A prave v tomto pripade je lepsie pouzit UtcNow kvoli zmene zimneho a letneho casu. Lebo sa ti moze stat, ze namiesto toho, ze to v cache bude 2 minuty sa moze stat, ze to tam bude 1 hodinu a 2 minuty.
Ale inac celkom pekne ta funkcia ako parameter na vytvorenie objektu ak nie je v cache.
 
duracellko, pred 4 mesiacmi
2
Neber to prosím jako útok - jen chci poradit...

1) Pokud člověk nepřijde k zajetýmu projektu, držel bych se konvencí běžně používaných v .NETu. Tzn. názvy parametrů by měly začínat malým písmenem.

2) Statické metody jsou zlo. Jediné místo, kdy jsem jejich existenci tolerovat, jsou jednoduché extenzní metody, příp. nějaké velmi jednoduché metody, které především nemají žádný "externí stav".
A proč jsou zlo? Protože v kódu, který je používá, nejde jejich volání "zamockovat" (zapomeňme na ošklivé hacky), takže takový kód používající volání statických metod je jen velmi těžko testovatelný.

Další problém je ten, že člověk na první pohled nevidí, že třída nějakou statickou metodu používá - to musí jít až na úroveň kódu.

Kdybys měl tuhle cachovací funkcionalitu schovanou za nějakým rozhraním, které budeš předávat třeba přes konstruktor do dané třídy, tak bude na první pohled jasné, že ta třída využívá nějaký cachovací mechanismus.

3) Jak už psal duracellko, používat DateTime.Now se může vymstít a taky doporučuju DateTime.UtcNow. Resp. i to DateTime.UtcNow není jic jiného než volání statické metody, takže i přístup k aktuálnímu času bych skryl za nějaký pěkný interface.
V tomhle konkrétním případě by ale stačilo třeba předávat jen TimeSpan, jak dlouho se to má držet v cache.
 
Augi, pred 4 mesiacmi
3
Duracellko a Augi som rád, že ste napísali.

Augi:

1. Ďakujem, toto si vezme k srdcu (tu mám veľké nedostatky), budem to cez víkend aktualizovať.

2. Ja používam veľa statických metód (väčšinou ako extension). A práve jednoduché metódy sú aj tie, ktoré som uviedol v mojom blogu. Ja ich mám v projektoch trošku inak zadeklarované, fungujú na princípe rozhrania, ale rozhraním nie sú. Je to v celku komplikované písať do blogu, ale funguje to jednoducho. Používam aj veľa rozhraní, ale všetko skrývať za ne určite nechcem.

A teraz si sadni, aby si nespadol. Ver či nie - projekty, ktoré vytváram (ZATIAĽ) nepotrebujú (uni)testy, nepotrebujú IoC a nepotrebujú mnoho ďalšieho. Na moju prácu mi stačí pár classov, pár statických funkcií a všetko ide ako má. Klient spokojný, ja spokojný, server spokojný. Nechcem aby vznikol na mojom blogu flame, rád sa učím novým veciam, som otvorený novým technológiám - ale zatiaľ ma nič nepresvedčilo, že napr. (uni)testy sú NÁM na niečo dobré (... napr. že by nám priniesli väčšiu efektívnosť a znížili náklady na tvorbu webu).

Duracellko a Augi:

3. Tie dátumy sú haluz. Používam DateTime.Now roky, nemal som zatiaľ problém so zimným a letným časom. Servre sú nastavené na časové pásmo Belehrad, Bratislavy, Budapešť ... takže tu by sme nemali mať problém (aj som všetko testoval a všetko funguje normálne, podľa nášho dátumu a času).

Duracellko:
Ďakujem (posledná veta sa mi páči) :-)
 
Peter Širka, pred 4 mesiacmi
4
2. Já používám taky hodně statických metod - hlavně těch z LINQu ;-) Ale mých vlastních statických metod mám minimum...

Nemám vůbec nic proti tomu, když v nějakém malém projektu žádné testy nejsou. Ale z hodně malých projektů se časem stane velký projekt, který už testy potřebovat bude. Sám píšeš, že "zatím" testy nepotřebuješ.

Moje výtka se teda vůbec netýkala toho, že nemáš testy (což jsem ostatně ani nevěděl), ale toho, že ten kód (resp. ten kód, který tohle používá) je špatně testovatelný.
Až se dostaneš do fáze, že testy potřebuješ, tak co budeš dělat s takovýmhle netestovatelným bastlem? Zkusíš napsat nějaké testy, ale zjistíš, že to jde jen velmi ztuha. A buď na testy zanevřeš nebo budeš muset kód přepsat tak, aby byl testovatelný (tedy mj. "vše schované za rozhraním") a psaní testů nebylo utrpení.

Jasně, pokud chceš pořád bouchat weby, tak bude asi nejefektivnější naprasit všechno do pár statických metod, a ten slepenec prostě bude fungovat. Ale pokud chceš někdy dělat něco většího, tak je dobrým startovním bodem začít psát i malé webíky pořádně (i když to ze začátku bude stát víc úsilí).

Ohledně přínosu testů (z vlastní zkušenosti):
1) Lepší znovupoužitelnost dobře testovatelného kódu. Tedy kód, který jsem schopen jednoduše otestovat jsem schopen také jednoduše znovu použít.
2) Můžu si dovolit udělat zásadní změny a testy mi pohlídají, že jsem nikde něco (ne)rozbil.
3) Při vývoji můžu jednoduše otestovat malou část kódu v izolaci. Nemusím pouštět IIS ani Cassini atd.
4) Kód, který dodávám do "produkce" prakticky nikdy neobsahuje chyby (velká změna oproti časům, kdy jsem netestoval).

3. Záleží, k čemu všemu ten DateTime.Now používáš. Pokud jen na nějakou cache na webu, kam při změně času stejně nikdo nechodí, tak ten problém asi nikdy nenastal (nebo si toho nikdo nevšiml).
 
Augi, pred 4 mesiacmi
5
2. "tak bude asi nejefektivnější naprasit všechno do pár statických metod, a ten slepenec prostě bude fungovat" - takto som to vôbec nemyslel. Takto weby nerobím, môj web má hlavu a pätu, aj vo výsledku a aj po kódovej stránke. Dávam si záležať na každom webe a každý nový web je vždy lepší. Nesúhlasím, že unitest urobí aj malý web poriadnym ("i malé webíky pořádně"). Pri zložitejšej logike, pri zložitých objektoch súhlasím s testovaním, ale inde je to zatiaľ pre mňa zbytočnosť. Stále sa bavím v rovine komerčných webov (nie WIN aplikácie, nie intranet a nie stránky ala Facebook, Google, Amazon, Seznam). Zistil som, že slovom TEST sa dá pekne chytať za slovíčka :-)

Ak máš nejaký odkaz na o(uni)testovanú stránku, s IoC a neviem s čím ešte, budem rád ak my pošleš odkaz, rád si ju pozriem v čom je lepšia od mojej tvorby. Budem ju analyzovať.

3. defaultne používam System.Web.Caching class Cache (http://msdn.microsoft.com/en-us/library/system.web.caching.cache.add.aspx), takže malo by to brať systémový čas. Vysvetľujem si to tak, že ak zadám expiráciu v DateTime.Now.AddMinute(2), tak expiruje mi to o 2 minúty (lebo Cache musí používať systémový dátum/čas a potom je jedno či je zima, leto, alebo prechodný rok).

 
Peter Širka, pred 4 mesiacmi
6
2. Stále si asi nerozumíme :-) Já neříkám, že je třeba psát testy. Jen říkám, že je třeba psát dobře testovatelný kód. Takový kód se totiž nejen dobře testuje, ale je přehlednější, lépe znovupoužitelný a má další pozitiva a sociální výhody ;-)

Nejlepší zdroje jsou IMHO od Miška Heveryho:
http://misko.hevery.com/code-reviewers-guide/
http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf
 
Augi, pred 4 mesiacmi
7
2. chápem, rozumiem a súhlasím.

Ďakujem za nejaké zdroje, prečítam.
 
Peter Širka, pred 4 mesiacmi
8
ja len k tej zmene casu.. problem je, ze bug nastane len raz do roka aj to v noci medzi 2hou a 3tou hodinou. Takze si ho asi nikto nevsimol a asi ani nikdy nevsimne. Ale ver, ze tam je. Scenar je nasledovny:
letny cas
2:59: vlozis objekt do cache s expiraciou o 3:01
3:00: zmena na zimny cas, cize posunutie hodin na 2:00
2:05 zimneho casu: nacitas objekt z cahce, pretoze ma expirovat az o 3:01
takze takto mas objekt v cache az hodinu, aj ked si ho chcel mat len 2 minuty.

a este k tym statickym metodam. neviem, ako a v akych projektoch to pouzivas, ale vzhladom na typy projektov, co robis, si myslim, ze pouzitie statickych metod nie je zlo v tomto pripade.
podla mna ich cielom nie je abstrakcia od caching systemu ale naozaj len helper funkcie na jednoduchsi zapis.
ASP.NET od verzie 4.0 podporuju zmenu caching providera, takze Service Provider pattern je tymto implementovany. A predpokladam, ze pravdepodobnost, ze to Siro niekedy vyuzije je takmer nulova, takze s ASP.NET implementaciou tohto patternu by som sa uspokojil. Proste nemusi clovek definovat vlastne interface a IoC, ked to uz je v ASP.NET spravene. Len info pre Sira, ze zase v tom ASP.NET to az tak dobre nie je, lebo cele to je zavisle na HttpContext-e a teda nemoze to bezat mimo ASP.NET (napr. v NUnit programe). Ale to teba asi nebude nikdy trapit.

Len este jedno odporucanie k tym metodam. Mozno by som volanie CacheWrite dal priamo do CacheRead. Predpokladam, ze za kazdym, ked nie je nieco v cache, tak po nacitani sa to do cache zapise. A nemusis CacheWrite pisat do kazdej lambda funkcie. Tymto sa asi vacsina lambda funkcii stane jednoriadkovymi.
 
duracellko, pred 4 mesiacmi
9
Máš pravdu s tým časom, tak z toho som paf - neviem prečo som sa takto (ako ty) na to nezamyslel...

Metódy: áno dalo by sa to urobiť tak .. lenže ak nastane prípad, že chceš proste vypnúť cacheovanie v určitej časti, tak si to vieš priamo v tej časti zariadiť.
 
Peter Širka, pred 4 mesiacmi
10
Peter, nenechaj sa nalakat na zle chodnicky. ;-] Ja mam kopec servisnych a DAO tried, ktore maju vsetko v statickych metodach a rozhodne nerobim na malom projekte.
Tvorba softwaru nieje taka jednoducha, ze staci dodrziavat vsetky odporucania co najdem a robit vsetko "poriadne".
 
Looser, pred 4 mesiacmi
11
Ja sa nevzdám statických metód ani bohovi a dodám na záver: chlapi dôležitý je výsledok, nie technológia :-)
 
Peter Širka, pred 4 mesiacmi
12
@duracelko:
s tym UtcNow: lenze ked pouzije utcnow, ktory je oproti letnemu casu posunuty o hodinu, tak v tej cache bude o hodinu dlhsie(alebo kratsie), lebo predpokladam, ze aspnet cache pouziva vo vnutry DateTime.Now, nie?

@augi:
to, ze NUnit nevie testovat staticke metody je jeho obmedzenie, to neznamena, ze staticke metody su zle. Bola by velka chyba, keby sa to takto zovseobecnilo. Okrem toho, keby chcel v buducnosti testovat, tak sa to ta velmi jednoducho obist, tak ze si napise nestaticky wrapper. Staticke triedy pridavaju do kodu prehladnost a prehladnost = menej chyb pri zmene kodu.

Okrem toho, ako by si napisal testy pre tie Sirove metody, keby neboli staticke, kedze pouzivaju aspnet cache, ktoru si nunit z prsta nevycuca?
 
Liero, pred 4 mesiacmi
13
@Liero.. dobry point.. teraz na to pozeram.. som myslel, ze ked porovnavam dva DateTime, tak ich najskor .NET sam skonvertuje na rovnaky typ (lokalny alebo UTC). Ale ako zistujem, tak to nerobi. No a ASP.NET Cache pracuje interne s UTC casom a sam si to aj skonvertuje. Takze ked Siro pouzil DateTime.Now, tak ASP.NET si ho aj tak skonvertuje na UTC.
 
duracellko, pred 4 mesiacmi
14
Liero: Nevím, že bych někde mluvil o NUnit ani o tom, že statické metody nejde zamockovat (viz Moles) - ale to nepovažuju za čisté řešení (ale u legacy projektů se může hodit).

"Okrem toho, keby chcel v buducnosti testovat, tak sa to ta velmi jednoducho obist, tak ze si napise nestaticky wrapper."
Ok, takže ještě jednou. Nemám ani tak problém s tím, že je něco implementované ve statické třídě/metodě, ale s tím, když se to odněkud napřímo používá. Pak totiž není z hlavičky "závislé" třídy jasné, že používá tu statickou třídu/metodu. Ten wrapper by tam měl být od začátku.

"Staticke triedy pridavaju do kodu prehladnost a prehladnost = menej chyb pri zmene kodu."
Jaký máš důkaz? Já jsem problémy se statickými metodami popsal ve svém prvním příspěvku (bod 2).

"Okrem toho, ako by si napisal testy pre tie Sirove metody, keby neboli staticke, kedze pouzivaju aspnet cache, ktoru si nunit z prsta nevycuca?"
Takže ještě potřetí - nejde o to, že by ta statická implementace nebyla testovatelná (to je), ale není unit-testovatelný kód, který tenhle statickej bazmek napřímo používá.
Jinak kód už přímo závislý na nějaké konkrétní implementaci se testuje pomocí integračních testů.
 
Augi, pred 4 mesiacmi

Nevyplnil si všetky povinné políčka alebo si ich vyplnil chybne. Neojebávaj môj systém.
Skontroluj či si zadal meno a komentár.
Tvoj komentár bol úspešne odoslaný.
Ďakujem.

Meno:

Komentár:

Ďalšie blogy
Dynamické vytvorenie subdomény v ASP.NET / MVC
Dynamické vytvorenie subdomény v ASP.NET / MVC
Bolo to už dávno, čo nás na firme žralo, že naša konkurencia vedela riešiť dynamicky vytvárenie subdomén alebo domén 3 úrovne. Google ich lepšie indexoval (tak sa nám to zdalo) a riešenie prišlo až v IIS 7.
Prečítať 9
ASP.MVC - Session troška inak a prihlásený užívateľ
ASP.MVC - Session troška inak a prihlásený užívateľ
V dávnej minulosti som používal vstavaného Session providera. Raz mi praskli nervy a rozhodol som sa experimentovať. Stálo to za to. Ten kto neexperimentuje ten nemá a ja experimentujem len pre moje potreby.
Prečítať 21
ASP.MVC, ASP.NET Facebook like count
ASP.MVC, ASP.NET Facebook like count
Jednoduchý kód na zistenie počtu like-kov z Facebooku na zadanú URL adresu. Je možné získať ešte ďalšie zaujímavé počty, napríklad počet zdielaní, počet komentárov, atď..
Prečítať
jQuery webcam plugin / ASP.NET, ASP.MVC
jQuery webcam plugin / ASP.NET, ASP.MVC
Pre náš webový projekt som použil jQuery webcam plugin, ktorý spustí Flash animáciu a odfotí užívateľa. Následne spracovaný obrázok odošle na server. Problém je, že odosiela data po pixeli.
Prečítať
UPDATE: Facebook login button ASP.NET a ASP.MVC
UPDATE: Facebook login button ASP.NET a ASP.MVC
Musel som napísať novú autentifikáciu, pretože vo Facebooku to zmenili zo dňa na deň. Takže teraz prinášam UPDATE, v ktorom vieme získať už aj e-mailovú adresu alebo lokalitu užívateľa.
Prečítať 3