Portál AbcLinuxu, 30. dubna 2025 15:37
Vývoj platformy .Net a jazyka C# sleduji od naprostých prvopočátků. Dílo Anderse Hejlsberga je se mnou v podstatě celý můj programátorský život (od Turbo Pascalu přes Delphi až po C#).
Proto jsem si nemohl ujít přednášku tohoto velkého makáče o C# 4. Trochu jsem se děsil, na počátku byl C# poměrně minimalistická evoluce adresující některé nedostatky Javy. Dvojka přidala hlavně generické konstrukce, což byl lety prověřený princip.
Zato třetí verze mi vždycky přišla šílená. LINQ jsem dodnes pořádně nestrávil, neměl jsem možnost si to pořádně osahat, protože se vezu na svobodném operačním systému a vlastně ani s Monem poslední roky moc neexperimentuji. Nepochybuji, že LINQ umí být užitečné v prostředí podnikových aplikací hemžících se XML a databázemi, nejsem si ale jistý, jestli je to dostatečně univerzální, abych to chtěl mít přímo v jazyce. Některé funkcionální rysy C# 3 se hodí na paralelizaci à la Haskell. Haskell je ovšem se svým přísným oddělováním čistého funkcionálního kódu a kódu s vedlejšími efekty trochu jinde.
C# 4 dopadl IMHO dost dobře. Klíčová věc je nové klíčové slovo dynamic
, které v podstatě umožňuje "duck typing" bez nějaké nepříjemné práce s reflexí. Neplést s klíčovým slovem var
, které umožňuje vynechat u deklarace typ, ovšem tento typ musí být staticky odvoditelný během překladu. Dynamická deklarace odkládá veškeré typové záležitosti až na čas běhu (a tím pádem zde nefunguje ani doplňování kódu).
Dynamické typy zjednoduší interakci s objekty vytvořenými v klasických dynamických prostředích jako Python (IronPython), Ruby (IronRuby) nebo JavaScript. Anders to demonstruje na několika hezkých příkladech, v jednom dokonce vezme kus JavaScriptového kódu a několika málo úpravami z něj udělá validní C# kód (za potlesku obecenstva).
Toto kachní typování se taky hodí při práci s XML. Něco takového:
doc.GetElementByName("xyz").GetLength()
můžeme přepsat třeba takhle:
doc.Elements.xyz.GetLength()
Z dalších novinek určitě potěší podpora výchozích argumentů funkcí a jmenné argumenty. C# 4 žádná šílená jízda není a je to tak dobře. Nějakou zlepšenou podporu dynamického kódu má přinést i nová Java, tak uvidíme. Microsoft se s .Netem docela snaží, otázkou je, jestli to s tím C# 3 dokonce nepřehnal.
Tiskni
Sdílej:
Nějakou zlepšenou podporu dynamického kódu má přinést i nová JavaAle jenom na úrovni bajtkódu, ne v jazyce. Hezké čtení o
invokedynamic
sepsal Charles Nutter.
Hejlsberg se rozjel, co na to říct jiného. A javisti se urputně, zatvrzele, zatrputile, zavile, zarytě brání jakýmkoli novinkám v jazyce. Když vidím, jaký rozruch mohou vyvolat pitomé lexikální uzávěry, nemůžu být optimista: budoucnost nám utíká mílovými kroky. Narozdíl od C#. Díky za článek.
public enum Singleton { INSTANCE; ... }Není to geniální?
enum
. Pokud použijete obyčejnou třídu, musíte navíc napsat jen vyvolání konstruktoru, ale zase vám kompilátor nevytvoří zbytečně navíc pole prvků výčtu.
Asi máme různé představy o tom, co ne to singleton. Já bych třeba třídu, která je jednou v paměti a pak ještě někde serializovaná (= má minimálně dvě instance) singletonem nenazýval.Pole bajtů, ze kterého lze deserializovat objekt, jaksi nejsem schopný považovat za instanci třídy, protože s ním prostě nejde dělat nic jiného, než právě deserializovat. A použitím enum-singletonu zajistíte, že ať budete deserializovat jak chcete, nikdy nedostanete jinou instanci než tu, která už existuje. Ne že by to nešlo naprogramovat ručně, ale je to pěkná magie.
transient
. Jenomže takovou chybu bych mnohem raději odhalil, než že se ji budu pokoušet zamaskovat. Ručně to naprogramovat není žádná magie, stačí neoznačit třídu singletonu interfacem Serializable
.
Třeba proto, že úplně zadarmo získáte bezpečnou serializovatelnost? Myslím, že byly i nějaké další důvody, ale tohle si vybavuju jako první.Jo, kdyby. Na první pohled si můžeme říci "Corba, IIOP, koho to zajímá" - zajímá to všechny co používají aplikační server jisté nejmenované třípísmenné modré firmy (ne, nemyslím ODS).
Hejlsberg se rozjel, co na to říct jiného. A javisti se urputně, zatvrzele, zatrputile, zavile, zarytě brání jakýmkoli novinkám v jazyce. Když vidím, jaký rozruch mohou vyvolat pitomé lexikální uzávěry, nemůžu být optimista: budoucnost nám utíká mílovými kroky. Narozdíl od C#. Díky za článek.Řekl bych, že vaše tvrzení pro většinu javistů ani zdaleka neplatí. Alespoň já se nebráním lexikálním uzávěrům v Javě proto, že by to bylo něco nového, ale proto, že je to podle mne zbytečnost, která na jednu stranu změní jazyk a zavede spoustu nových věcí, se kterými je potřeba počítat, ale jinak nepřinese programátorovi nic nového – maximálně ušetří IDE vygenerování 2 klíčových slov. Na druhou stranu bych rád v Javě viděl statickou reflexi ze tříd (
něco.class
) rozšířenou alespoň na properties a nebo klidně i na metody a fieldy. K tomu nejsou teoreticky potřeba žádné zásahy do jazyka, dalo by se to řešit i pouhou knihovnou, podpora kompilátoru by byla lepší a i případný zásah do jazyka by pouze kopíroval současné konstrukce.
nepřinese programátorovi nic novéhoVůbec ne. Tak proč už dneska nejsou na kolekcích iterátorové metody? (Rada: s anonymními třídami by je stejně nikdo nepoužíval.) V knihovnách (Commons Collections i Google Collections) nějaké snahy jsou, ale výsledný kód je prostě nechutný. Stejně tak lze porovnat příklady použití frameworku Fork-Join (taky budoucí součást Javy 7) s uzávěry a bez nich. A dalo by se pokračovat.
ušetří IDE vygenerování 2 klíčových slovChcete říct vygenerování čtyř řádků syntaktického balastu, že?
Na druhou stranu bych rád v Javě viděl statickou reflexi ze tříd (něco.class
) rozšířenou alespoň na properties a nebo klidně i na metody a fieldy. K tomu nejsou teoreticky potřeba žádné zásahy do jazyka, dalo by se to řešit i pouhou knihovnou, podpora kompilátoru by byla lepší a i případný zásah do jazyka by pouze kopíroval současné konstrukce.
Reference na metody a vlastnosti by nebyly špatné. S vlastnostmi obecně si nejsem jistý, mně osobně by stačilo automatické vygenerování getterů a setterů, ale programátoři okýnkových aplikací by je určitě užili, včetně bindování a dalších sprostých slov.
Ale pořád si myslím, že konzervativismus javistů je obrovský, řada lidí se ještě nevzpamatovala z novinek v Javě 5. A sýšarpisti zatím efektivně zužitkovávají výsledky výzkumu programovacích jazyků v Microsoft Research. Podle mě je to škoda.
Tak proč už dneska nejsou na kolekcích iterátorové metody? (Rada: s anonymními třídami by je stejně nikdo nepoužíval.)Protože by byly k ničemu? Co můžete v Javě s iterátorovou metodou udělat jiného, než s rozhraním
Iterable
?
Iterable
, jsou akorát škrábání se levou nohou za pravým uchem.
if (list.any({ Integer num => num > 0})) { ... }
. Ve staticky typovaném jazyce si nedovedu představit nic o moc srozumitelnějšího (dobře, v C# 3 je to o něco lepší Hledat? Filtrovat? Konvertovat? Atd. atd. A takové fígle, jako že na to lze implementovat vlastní Iterable, jsou akorát škrábání se levou nohou za pravým uchem.Není to škrábání se levou nohou za pravým uchem, je to normální implementace. Jediná „výhoda“ uzávěrů je v tom, že programátorovi zatají, že se tam uvnitř vlastně něco děje, a programátor bude mít pocit, že filtrovat kolekci 1000× místo aby si uložil její filtrovanou kopii je normální. Tohle se může hodit v jazycích s vysokou úrovní abstrakce, ale ne v nízkoúrovňovém jazyce, jako je Java.
if (list.any({ Integer num => num > 0})) { ... }. Ve staticky typovaném jazyce si nedovedu představit nic o moc srozumitelnějšíhoJá si tedy dovedu představit spoustu srozumitelnějších věcí. Tenhle kód považuju za naprosto nesrozumitelný a trvalo mi dost dlouho, než jsem zjistil, co to asi tak má dělat. Když jsem to konečně rozluštil (hm, měl jsem si ten odstavec radši rovnou přečíst do konce), je to konstrukce, kterou buď použijete jednou, a pak ty ušetřené dva řádky nestojí za to, nebo ji budete používat opakovaně, a pak vás uzávěra bude pouze svádět k tomu kopírovat kód, místo abyste použil normální volání podprogramu.
Když jsem to konečně rozluštil (hm, měl jsem si ten odstavec radši rovnou přečíst do konce), je to konstrukce, kterou buď použijete jednou, a pak ty ušetřené dva řádky nestojí za to, nebo ji budete používat opakovaně, a pak vás uzávěra bude pouze svádět k tomu kopírovat kód, místo abyste použil normální volání podprogramu.Těch ušetřených řádků je asi sedm (záleží, samozřejmě, na formátování kódu). Plus, a to je mnohem důležitější, nesvádí ke kopírování kódu. V současné době totiž nelze použít volání podprogramu a vůbec to zobecnit. Existují v dvě principiální možnosti jak se kopírování kódu v tomto případě vyhnout: zobecnění kódu (C makra, LISP makra, C++ šablony) a kompozice kódu (closure, lambda, anonymní třída).
for (Integer num : list) { if (num > 0) { … break; } }Přidané řádky jsou tučně a jsou 3 (někdy 2, když dáte místo
break
u rovnou return
).
V současné době samozřejmě lze využít volání podprogramu. Pokud budete často používat testování, zda kolekce obsahuje nějaké kladné či záporné číslo, můžete si napsat třeba takovýhle kód:
public interface Filter<T> { public boolean filter(T item); } public class ListUtil { public boolean any(Iterable<T> iterable, Filter<T> filter) { assert iterable != null; for (T item : iterable) { if (filter.filter(item)) { return true; } } return false; } public boolean all(Iterable<T> iterable, Filter<T> filter) { assert iterable != null; for (T item : iterable) { if (!filter.filter(item)) { return false; } } return true; } } public enum OriginFilter implements Filter<Integer> { Positive { public boolean filter(Integer num) { assert num != null; return num > 0; } }, Negative { public boolean filter(Integer num) { assert num != null; return num < 0; } }, PositiveOrZero { public boolean filter(Integer num) { assert num != null; return num >= 0; } }, NegativeOrZero { public boolean filter(Integer num) { assert num != null; return num <= 0; } }, ; }A pak už jej budete jenom používat:
if (ListUtil.any(list, OriginFilter.Positive)) { … }Když si pak vzpomenete, že v té kolekci také může být
null
, opravíte třídu ListUtil
, a/nebo OriginFilter
, a máte vystaráno. Jako bonus máte to, že v tomhle kódu zjistíte hned, že máte v kolekci null
, se kterým jste nepočítal, v kódu s uzávěry budete bádat nad tím, který ze spousty výrazů na tom řádku vlastně NPE vyhazuje.
Interface Filter<T>
pak můžete třeba spojit s java.util.concurrent.Callable, a hned můžete filtry snadno použít při vícevláknovém zpracování. Přičemž to vše je možné napsat a zařadit do standardní knihovny teď hned, bez jakékoliv změny jazyka.
1 if (list.any({ Integer num => num > 0})) { 2 ... 3 }
1 for (Integer num : list) { 2 if (num > 0) { 3 … 4 break; 5 } 6 }Řádku 1 v původním kódu odpovídá řádek 2 v mém, bloku na řádku 2 původního kódu odpovídá blok na řádku 3 v mém kódu. Přidaný je pouze cyklus
for
a break
. Žádná proměnná s výsledkem není potřeba (byla by potřeba, pokud bych chtěl mít i větev else
).
| any (>0) list = ...
Ve staticky typovaném jazyce si nedovedu představit nic o moc srozumitelnějšíhoA dovolím si tvrdit, že to, co jsem napsal, je srozumitelnější.
To mi nepřipomíná žádný jazyk, který jsem kdy vidělHaskell
namespaces
. Člověk chce použít nějakou jednoduchou funkci, nemůže ji najít, tak jde do MSDN a zjistí, že ta funkce je něco jako System.xxx.xxx.xxx.xxx(struct * xxx)
tak ještě hledá definici příslušného struct
a pak se musí votravovat s vyplňováním toho structu
using System.Globalization; float.Parse("1.1", CultureInfo.InvariantCulture)
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.