Slidy a příklady z přednášky/cvičení - léto 2011/2012:
System.Globalization.CultureInfo jako implementace IFormatProvider a ISO kód jazyka • nastavení Thread.CurrentCulture a Thread.CurrentUICulture • rozšířená varianta převodu na řetězec: IFormattable.ToString(string format, IFormatProvider) ♦ třídění řetězců a národní prostředí - chování String.Compare/CompareTo • problém s invarianty v datových strukturách po save→load •
CultureInfo.InvaliantCulture jako řešení • StringComparer.InvariantCultureIgnoreCase/OrdinalIgnoreCase • String.CompareOrdinal vs. String.Compare ♦ problém s malými/velkými písmeny: Ť → ť nebo problém tureckého I/i • *.ToLowerInvariant/ToUpperInvariant ♦
Unicode a jeho kódování • .NET znak = System.Char = 2 bytes • UTF-8 vs. UTF-16 a escape sekvence • problém s combining characters a rozdílnými reprezentacemi stejného grafému • třída System.Globalization.StringInfo (1 TextElement = 1 grafém) a .LengthInTextElements, .SubstringByTextElements a statické .ParseCombiningCharacters (indexy za začátky grafémů), .GetNextTextElement (jako indexer na System.Stringu), .GetTextElementEnumerator • různé Unicode zápisy a .CompareTo vs. .Equals vs. == • pozor na různé typy mezer → char.IsWhitespace() • pozor na verze Unicode
CultureInfo, nastavení Thread.CurrentCulture a využití IFormattable.ToStringnameLabel v aplikaci) vs. znaky reprezentující kompletní grafém (nameTextbox v aplikaci) a využití StringInfo pro parsování jednotlivých grafémůAssembly.Load vs. Assembly.LoadFrom vs. Assembly.ReflectionOnlyLoad* • definice vlastních atributů (třídy Attribute a AttributeUsageAttribute) • srovnání rychlosti reflection s ostatními mechanismy volání metod ♦
zopakování Stack Machine vs. Register Machine • základní přehled CIL (MSIL) - instrukce ld*, st*, call, br, ret, add/sub/mul/and/..., *.i1/2/3/4.../r4/r8.*, *.0/1/2/4 • srovnání s Java bytecode a Dalvik VM (viz např. Wikipedia) bytecode • IL assembler a jeho využítí • nástroje ilasm.exe, ildasm.exe ♦
třídy z Reflection.Emit a generování kódu • knihovna Mono.Cecil (http://www.mono-project.com/Cecil, stažení https://github.com/jbevain/cecil) - lze použít i pod Microsoft .NET (nemá závislosti na Mono) - Cecil ⊇ System.Reflection (ale Cecil nenahrává assemblies a kód do aplikační domény/appdomain (CLR)): třídy AssemblyDefinition ≅ Assembly, TypeDefinition ≅ Type, *Definition ≅ *Info, využití Cecil pro modifikaci kódu/code injection (využití např. pro Aspect Oriented Programming (AOP) - ukázka logging a caching aspektů) ♦
lambda funkce → Expression Trees • využití Expression Trees pro dynamické generování kódu ♦ poznámka k bezpečnosti string.Format("...{0}", str) vs. string.Format("..." + str), resp. výhodám "...{0}", i vs. "..." + i.ToString()
AppDomain • vlákna a appdomény ♦
binární serializace System.Runtime.Serialization (BinaryFormatter, nepoužívat obsolete SoapFormatter) - serializuje pouze instanční datové položky (private, protected i public) • atribut [Serializable] • opt-out pomocí [NonSerialized] • [OnDes/Serializing/ed] • ISerializable, SerializationInfo •
textová identifikace plného jména každého serializovaného typu (včetně jména assembly) → automatické nahrání assembly do appdomény (výhody a nevýhody) • jména datových položek ve streamu → version tolerant serialization (data ve streamu navíc se ignorují + [OptionalField]) • podpora pro neserializovatelné typy + změna způsobu serializace pro serializovatelné typy: ISerializationSurrogate → IFormatter.SurrogateSelector a metoda .AddSurrogate() •
výběr/záměna serializovaných typů a jejich zdrojových assemblies: abstraktní třída SerializationBinder → IFormatter.Binder a metody BindToType (jméno assembly + jméno typu ⇒ instance třídy Type), BindToType (instance třídy Type ⇒ jméno assembly + jméno typu) ♦ nepoužívat XML serializaci (třída XmlSerializer) - pouze public data, nepodporuje cykly ♦
přehled DataContracts serializace (více bude na přednášce o WCF) • DataContractSerializer ♦
volání mezi aplikačními doménami ♦ MarshalByRefObject (MBRO) ♦ .NET Remoting • RemotingSurrogateSelector
MarshalByRefObject objektechLauncher.exe).config konfiguračního souboru aplikace)PeVerify.exe (Visual Studio 2010 Command Prompt) pro předběžnou verifikaci jakou provádí JITGCHandle ♦ CLR hosting • native exports from managed assembly • COM Callable Wrappers (CCW) ♦ P/Invoke
SecurityCritical vs. SecuritySafeCritical vs. SecurityTransparent):
stackalloc • fixed pole ♦ volací konvence (cdecl, stdcall, fastcall, thiscall) • callbacky z nativního kódu do managed kódu • GC.KeepAlive() ♦ IDisposable a jeho implementace na třídách s nativními zdroji ♦ Runtime Callable Wrappers (RCW) ♦ IntPtr a SafeHandle ♦ C++/CLI
IntPtr jako platformě nezávislý typ s velikostí nativního ukazatele • rozšíření IL pomocí modopt • nastavení volací konvence u .NET metody - anotací IL kódu pomocí modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) (nepodporuje C# překladač ⇒ řešení C#→assembly→IL assembler→opatchování→assembly, tj. csc.exe→ildasm.exe→notepad.exe→ilasm.exe nebo vygenerování wrapper metody pomocí System.Reflection.Emit) • volací konvence v C++/CLI ♦
Managed Addin Framework (MAF) - zběžný přehled: System.AddIn.dll • [AddIn] • AddInStore • cíl: společná appdoména vs. appdomain per addin vs. process per addin ♦
Managed Extensibility Framework (MEF) • v OOP "běžný" způsob svázání instancí pomocí new a předávání referencí v kódu vs. dependency injection • MEF jako dependency injection framework • MEF part (komponenta) jako třída připravená na kompozici • [Export] a [Import] a definice kontraktu • katalogy: TypeCatalog, AssemblyCatalog, DirectoryCatalog, AggregateCatalog • kompozice v kontextu CompositionContaineru • .GetExport<T>() a .SatisfyImportsOnce() • [ImportMany] • creation policy ♦
early vs. late bound: nevirtuální volání → virtuální metody (napůl: early - index ve VMT, late - konkrétní tabulka VMT) → typ dynamic (zcela late) • Dynamic Language Runtime (DLR) jako nadstavba CLR • IDynamicMetaObjectProvider → DynamicMetaObject → .Bind*() • standardní DLR implementace ExpandoObject • základ DLR scripting API a hostování dynamických jazyků • ScriptEngine a ScriptScope (.Get/SetVariable()) a .Execute() • projekt Roslyn - skriptovaný C# ♦
Transakce v .NET (připomenutí transakcí obecně, ACID vlastnosti) • ambientní transakce jako Transaction (.Rollback()) • resource managers ← IEnlistmentNotification (two-phase commit - možnost i pouze jednofázového commitu) • příklady resource managerů (DB nebo FileSystem) a způsob fungování v transakcích • TransactionScope a .Complete()/.Dispose() • volatile resource managers
ImportMany)dynamic proměnnýchdynamic formálních parametrů), ale s předáním dynamic skutečného parametruExpandoObjectu z DLR a jeho využití s typem dynamicBasicHttpBinding vs. WSHttpBinding vs. WebHttpBinding vs. NetTcpBinding)
Slidy a příklady z přednášky - zima 2011/2012:
.exe.config soubory ilustrujici možnost vynucení konkrétní verze CLRSystem.Diagnostics.Stopwatch pro rychlé microbenchmarky - úkol změřit zda je rychlejší metoda Class1.m() nebo Class2.m(). Sledujte chování 32-bit/64-bit JITu (čas překladu, spotřeba paměti).OutOfMemoryException • standardní halda nefragmentovaná → alokace = přičtení k ukazateli • princip generačního GC (výhody/nevýhody) • 3 generační GC na standardní haldě • heap compacting • alokace od OS po segmentech (16 MB na workstation) •
ephemeral segment (gen0 + gen1) • pozor na data předávaná nativnímu (neřízenému) kódu (pinning → fragmentace haldy po GC) • alokace X na haldě: 8 B (4 B = ukazatel na VMT, 4 B = syncblock viz vlánka) + sizeof(X) • nulování alokovaných dat • workstation (1 sada hald) vs. server (1 sada hald per CPU) • spouštění GC (spuštěné alokací, nedostatkem paměti v systému, ručně) • výjimečně užít: GC.Collect() a GC.Collect(generace), kolekce 0 = gen0, 1 = gen0+gen1, 2 = gen0+gen1+gen2+LOH ♦ finalizace objektů: finalizer (C#: ~X ↔ .NET: protected virtual void System.Object.Finalize()), pozor není destruktor • finalizační vlákno (finalization queue, freachable queue - GC root) • GC.WaitForPendingFinalizers() • z finalizeru nepoužívat jiné objekty na haldě a proč (př. problém s nevoláním .Close()/.Dispose() na StreamWriter + FileStream) → finalizer pouze pro uvolňování neřízených zdrojů (viz NPRG038 v LS) ♦
jak se chovat ke GC haldě: pozor na velké objekty (pole) - reuseovat • rozlišovat long live vs. short live objekty • data se stejnou životností alokovat najednou • pozor na finalizované objekty (přežití GC) • vejít se do gen0, neprodlužovat životnost (cacheovaní) • pozor na pinned objekty (alokovat brzy)
WeakReference a její použití (.Target) ♦ shrnutí rozdílů 32-bit vs. 64-bit CLR (2/2 GB virtuální adresový prostor v 32-bit režimu → max. 1,44 GB managed heap, i v režimu 3/1 GB) ♦ alokace na zásobníku: limit velikosti zásobníku - ve Windows (PE) hlavičkách spustitelné assembly, implicitně 1 MB stack reserve (max. velikost) a 4 kB stack commit (počáteční velikost), viz dumpbin.exe, resp. editbin.exe z VS2010 command line tools ♦ pozor: 1 stack per thread → nebezpečí vyčerpání virtuálního adresového prostoru ♦
srovnanání .NET GC a Mono sgen GC (2 generace, 4 MB segmenty, skoro "malloc" style gen1, LOH >8000 B) ♦ předávání parametrů hodnotou a odkazem (klíčové slovo ref, tracking reference vs. C ukazatele, resp. C++ reference) ♦ platnost lokálních proměnných (nereusovat, minimalizovat platnost), neinicializované lokální proměnné ♦ výstupní parametry (klíčové slovo out) ♦
dědičnost (inheritance): kompatibilita typů, dědění členů (metod), explicitní konverze (T) x, InvalidCastException ♦ sealed typy ♦ boxing/unboxing (unboxovací konverze nezahrnuje jiné, ani implicitní, konverze; hodnotové typy na managed haldě) ♦ volání metod na hodnotových typech (nedochází k boxingu, klíčové slovo this) ♦ metoda System.Object.ToString() ♦
třída System.Type (odkaz na VMT), zpřesnění pohledu na objekty na managed haldě (tj. instance referenčního typu nebo zaboxovaná instance hodnotového typu = 4 B reference na instanci Type + 4 B syncblock + vlastní data objektu), operátor typeof(...), metoda System.Object.GetType()
Type/typeof/.GetType() a konverze (přetypování) • (T) x → možná InvalidCastException ♦ kompatibilita typů s null • operátory is a as • null is X vs. null as X • is vs. ==/!= na Type vs. vztah dědičnosti IsSubclassOf ♦
viditelnost členů v typech (private/public/protected/internal/protected internal) • implicitní viditelnost v jednotlivých typech ♦ členění jmen typů (proč) • jmenné prostory (namespaces) = C# feature vs. vnořené typy (nested types) = CLR feature (v textové podobě oddělené + v CLR, . v C#) • plné jméno typu (.FullName) vs. jméno typu včetně zdrojové assembly (.AssemblyQualifiedName) → identifikace typu pro C# (jazyk) vs. CLR + C# (překladač) • aliasy • global:: alias • externí aliasy ♦ viditelnost nevnořených typů (internal/public) ♦
co obsahují typy: v základním pohledu data (instanční/statická) a metody (instanční/statické) • data a metody z pohledu dědičnosti ♦ member hiding (klíčové slovo new) • proč pouze warning &diams vlastnosti (properties) • syntaxe (nepovinný setter/getter, různá viditelnost settru a getteru, atd.) • proč používat • pár metod get_JménoVlastnosti/set_JménoVlastnosti • automaticky implementované vlastnosti ♦ inlinování metod JITem ♦
virtuální metody (virtual methods) • virtual → nová položka ve VMT (nová metoda) • override → přepsání položky ve VMT (nová implementace existující metody) ♦ abtraktní metody a třídy • abstract → virtual • možná implementace podmnožiny zděděných abstraktních metod → musí zůstat abstract class ♦ statické třídy (označené atributem static) • static class → sealed class
global::as, is), parametry metod se resolvují vždy za překladu (staticky) ♦ pravidla používání vlastností ("fast code") vs. metod ("slow code") vs. datových položek, viz např. .Count → vrací "nacachovanou" hodnotu vs. .Count() → opravdu to musí v cyklu spočítat (pro takovou metodu lepší pojmenování .GetCount() pro zdůraznění děje, viz dále) • jména metod začínat slovesem, tj. např. Get/Set/Add/Parse/Create/Send/... • jména vlastností = podstatné jméno • jména bool vlastností Ispřídavnéjméno/Haspodstatnéjméno/Cansloveso/apod. ♦
připomenutí virtuálních metod (static vs. dynamic dispatch) ♦ pozor na prioritu konverzního operátoru (přetypování) • přetypování nemodifikuje ukazatel za referencí (narozdíl od C++), tj. vždy ((Y) ((X) a)) ↔ ((Y) a) ♦ sealed metody + výhody jich a sealed tříd - vynucení static dispatch (nevirtuálního volání) ♦ volání implementace předka pomocí base • pozor: base.M() ≠ ((Predek) this).M() ♦
virtuální metody pouze public a protected a nelze u nich měnit viditelnost ♦ rozhraní (interface) - základní syntaxe a sémantika • rozhraní a is, as • členy z rozhraní lze implementovat virtuálními nebo nevirtuálními členy implementující třídy • nové vyplnění "interfacové VMT" při každé implementaci rozhraní v hierarchii tříd ♦
datové položky a jejich inicializace ♦ konstruktory • konstruktor se volá pouze v rámci volání new X(), tj. na rozdíl od C++ žádná speciální sémantika pro "copy kontruktory", apod. • C# překladač generuje konstruktory s implementací: inicializace mojich dat + konstruktor předka + tělo konstruktoru v kódu, CLR vyplňuje ukazatel na Type (VMT) • pozor - v Javě jinak, v C++ ještě jinak • :this a :base ♦
statické kontruktory (class constructor = static constructor = type initilizer) a jejich volání před prvním použitím třídy (generování nutného kódu JITem)
new, virtual/abstract, override) a ukázka nevirtuálního volání pomocí basebeforefieldinit a rozdíly ve volání statického konstruktoru • generování beforefieldinit překladačem C# • konstruktor = metoda .ctor, statický konstruktor = metoda .cctor ♦
inicializace struktur • bezparametrický konstruktor u struct • postupná inicializace položek struktury bez volání konstruktoru ♦ const členy v typech • readonly členy v typech • const vs. readonly static a využití rozdílů (verzování) ♦ návrhový vzor Singleton: jeho využití a implementace v .NET (odložená inicializace, využití WeakReference) ♦
virtuální metody podruhé • srovnání virtuálních metod v C# (.NET) vs. Java vs. C++ • klíčové vlastnosti virtuálních metod: (1) virtuální metoda jako slib (promise) korektního fungování při rozšíření, (2) verzování tříd a "fragile base class problem" [nutnost omezení breaking change], (3) rychlost volání (dereference skrz VMT, možnost inlinování) • nevirtualita jako metoda defensivního programování • zvirtuálnění původně nevirtuální metody není v C# breaking change ← C# překladač generuje callvirt IL instrukci (místo call - tu ale stále generuje např. při base.M()) i pro volání nevirtuální metody (callvirt na nevirtuální metodě JIT přeloží jako nevirtuální volání) ♦
pole • vícerozměrná pole • problém s rychlostí (kontrola mezí, vícerozměrná pole) • typické JIT optimalizace • výhody Array.Copy, Array.Clear, apod. • MS implementace: možnost vytažení kontroly mezí před cyklus ♦ zběžný úvod do generických typů
beforefieldinit C# překladačem + ukázka rozdílného chování tříd s/bez beforefieldinit v kontextu času volání statického konstrutorubeforefieldinit třídy, kdy nedojde k volání statického konstruktoru vůbec nikdyconst a readonly static položkami na dvou verzích aplikace a odkazované knihovní assemblyreadonly static • s odloženou inicializací • s dočasnou inicializací dle potřeby s využitím WeakReferencewhere (omezení přípustných typů = rozšíření možností použití typového parametru) • null a typové parametry ♦
generické metody • generické metody v rozhraních (dětičnost method constraints) • generická metoda z rozhraní vs. implementace negenerickou metodou • default(T) ♦ triky s generickými typy ♦ this[] = indexery v C# vs. parametrické vlastnosti v .NET (parameterful properties) • související problémy pro hodnotové typy (getter nemůže vrátit referenci na vnitřní hodnotu) ♦
rozšiřování rozhraní, tj. interface I1 {} interface I2 : I1 {} • explicitní implementace rozhraní • implementace generických rozhraní ♦
enumerátory • IEnumerable a IEnumerator a generické varianty • foreach cyklus • InvalidOperationException při současné enumeraci a modifikaci ♦ standardní BCL rozhraní pro kolekce • ICollection a IList a lépe navržené generické varinaty
IDictionary a generická varianta ♦ standardní kolekce v System.Collections.Generic (srovnání s alternativami v System.Collections) ♦ implementace typů pro bezproblémovou interoperabilitu s BCL kolekcemi i dalšími třídami z BCL ♦ rozhraní IDisposable (pro čistě managed typy jen volat .Dispose() na vnořených "disposables" - více o IDisposable při spolupráci s nativním kódem viz letní přednáška NPRG038) •
rozhraní IClonable ♦ metody System.Object • .MemberwiseClose() pro mělkou kopii • .Equals (implementace by měla být relace: symetrická, reflexivní, tranzitivní), pozor na parametr jiného typu nebo null • override Equals → implementace IEquatable<T> • rozhraní IEqualityComparer<T> a využití defaultní implementace EqualityCompaper<T>.Default • vhodná implementace .GetHashCode(): (1) rychlá [případně cacheovat], (2) správně [a.Equals(b) ⇒ a.GetHashCode() == b.GetHashCode(), (3) rovnoměrné rozdělení [ať Dictionary<T> nedegradujeme na LinkedList<T>], (4) hodnoty, které jsou klíčem musí být immutable • generování hashcode pro složitější typy ♦
rozhraní IComparable a IComparer a generické varianty ♦ přetěžování operátorů • operátory a constraints na typových parametrech generických typů ♦ konverzní operátory ♦ shrnutí "BCL friendly classes" ♦ implementace rozhraní strukturami • obecně hodnotové typy (i simple types jako int, long, atd.) a rozhraní ♦ foreach přesněji - optimalizace překladače (viz specifikace C#): volání GetEnumerator() i na typech neimplementujících IEnumerable (výhoda struct enumerátorů) ♦
extension metody
unsafe kód a nativní ukazatele - více viz letní přednáška NPRG038)try/finally vs. throw/catch (zvlášť pro systémové výjimky jako NullReferenceException: př. Windows/x86: jediná instrukce cmp bez jumpu na straně volání vs. při null dereferenci CPU interrupt→Windows SEH výjímka→CLR→throw new NullReferenceException()→sběr stack trace + odrolovávání zásobníku) • throw ex; vs. throw; v catch bloku • využití .InnerException • pozor na "StackOverflowException" • ladění výjimek ve Visual Studiu: možnost zapnutí přerušení laděného programu i při vyhození zachycené výjimky, viz menu Debug→Exceptions... ♦
vlákna a vícevláknové aplikace • připomenutí základů ze cvičení • .NET vlákno ≠ vlákno OS (viz MS SQL Server nebo ASP.NET - více viz letní přednáška NPRG038) • třída System.Threading.Thread • .Start() a stavy vlákna • pasivní vs. aktivní čekání • .Join(), Thread.CurrentThread, Thread.Sleep(), unikátní .ManagedThreadId, .Abort() → vyvolání výjimky ThreadAbortException → rethrow in catch → možnost Thread.ResetAbort() • chování try/finally vzhledem k ThreadAbortException ♦
zámky (klíčové slovo lock) • syncblock a dynamická alokace zámků • zámky podrobněji - "zámek" v .NET = monitor • třída Monitor a její využití pro synchronizaci (Wait/Pulse/PulseAll; .Wait(x) ⇔ .Exit(x); .InternalWait(x); .Enter(x);) • možné problémy se zamykáním: deadlock, livelock • pozor na překlad lock (x) {} na try/finally blok → může vést k odemčení datové struktury v nekonzistentním stavu • ukončení procesu s vlákny a vlákna na pozadí (.IsBackground) ♦
System.Threading.*Slim synchronizační primitiva jako dobrá (rychlá) alternativa k zámkům/monitorům (ReaderWriterLockSlim (.NET 3.5), SemaphoreSlim (.NET 4), ManualResetEventSlim (.NET 4) ♦
synchronizační primitiva poděděná od WaitHandle • implementována v OS: výhody (skupinové čekací metody, sdílení mezi procesy) a nevýhody (rychlost) • společné .WaitOne(), .WaitAll(), .WaitAny() • Mutex jako lock, Semaphore, AutoResetEvent, ManualResetEvent
finally a ThreadAbortException vzhledem k finally blokuSystem.Threading.Interlocked • připomenutí chování dnešních procesorů vzhledem k paměťovým přistupům (weak/strong memory model) • klíčové slovo volatile • sémantika volatile čtení/zápisu a důsledky • Thread.MemoryBarrier() a vztah k volatile ♦
vhodné a nevhodné užití zámků • proč ne lock (typeof(T)) • proč ne lock (this) ♦ System.Threading.ThreadPool ♦ metody s proměnným počtem parametrů ♦ iterátorové metody • výhody a nevýhody • yield return a yield break ♦
delegáti jako chytré ukazatele na metody • multicast na delegátech ♦ anonymní metody • anonymní metody jako closures (lokální proměnné nadřazené metody v anonymní metodě)
System.String) ♦ základy vlastních [atributů] - více v NPRG038 ♦ další standardní hodnotové typy (výčtové typy, nullable typy) ♦ variance (generických) typů (kovariance a kontravariance) ♦ ??? ♦ ??? ♦ Windows Forms (WinForms)
Anchor pro usnadnění tvorby oken s proměnnou velikostí, dále ukázka dialogů a uživatelsky definovaných ovládacích prvkůFlowLayoutPanel spolu s vlastností Dock pro jejich automatické uspořádání; obrázky ve zdrojích assembly [Resources] a třída ImageList)
Cvičení - zima 2011/2012:
Cíl hry: dostaňte slona úplně dolů.
Při programování mějte navíc na paměti:
Základní verze úlohy je za 10 bodů. Pokud si chcete procvičit WinForms a tvorbu GUI více nebo potřebujete získat další body navíc, můžete také naimplementovat některé z následujících rozšíření.
Readme.txt, kde bude uveden seznam implementovaných rozšíření a stručný popis řešení.Ctrl+F5), ladění - Run with Debugging (F5), Step into/over (F11/F10), breakpointy (vypínání, podmíněné), callstack, watche, Edit&Continue, IntelliSense (Ctrl+Space, Ctrl+Shift+Space) ♦
dokumentační komentáře ///: základní elementy, automatické zobrazení v IntelliSense, generování offline dokumentace pomocí SandCastle ♦
#region/#endregion
new StreamReader/StreamWriter(string fileName) ze System.IO ♦ nezapomínat volat .Dispose() (.Close()) na StreamReaderu a hlavně StreamWriteru ♦
pojmenovávání identifikátorů: privátní proměnné (data ve třídách + lokální proměnné) → camelConvention, jména typů + metod + veřejných položek → PascalConvention, víceslovné názvy a zkratky: firstSecondThird, FirstSecondThird, FirstIOThird, ioSecond, FirstHttpThird or httpSecond ♦
objektový návrh algoritmů
Napište program, který dostane jako argumenty příkazové řádky tři parametry: vstupní soubor, výstupní soubor a maximální šířku textu. Všechen text ze vstupního souboru přeformátuje do výstupního tak, aby každý řádek měl právě maximální šířku textu, je-li to možné.
Pokud program obdrží špatné množství argumentů nebo třetí argument není platné číslo větší než 0, program vypíše na standardní výstup řetězec "Argument Error". V případě, že vstupní nebo výstupní soubor nejde otevřít (vstupní soubor neexistuje, špatné znaky v názvu, nedostatečná práva, apod.), vypíše program řetězec "File Error".
Při formátování je potřeba dodržet následující pravidla:
'\n', '\t' a ' ') považujeme za bílé znaky, vše ostatní za znaky tisknutelné. Pro jednoduchost máte zaručeno, že znak '\r' v textu není.Výše uvedená pravidla je třeba dodržet velmi důsledně, protože výstup vašeho programu bude porovnán se vzorovým výstupem znak po znaku. Rovněž neděletje žádné předpoklady o velikosti vstupu. Celý vstup a dokonce ani jedniný jeho řádek se nemusí vejít do paměti.
$>program.exe plain.txt format.txt 17
Vstupní soubor plain.txt
If a train station is where the train stops, what is a work station?
Výstupní soubor format.txt
If a train
station is where
the train stops,
what is a work
station?
Příklad 2:
$>program.exe plain.txt format.txt abc
std. výstup
Argument Error
Console.In, Console.Out, Console.Error ♦ StreamReader je potomkem abstraktní třídy TextReader, také StringReader : TextReader ♦
TextWriter.Write(char[] text, int startIndex, int length) ♦ System.Security.SecurityException (bezpečnost na úrovni .NET/CLR) vs. System.UnauthorizedAccessException (bezpečnost na úrovni OS) ♦
Windows/Unix (Linux, MacOS X)/Mac oddělovač řádků, Environment.NewLine ♦ principiální rozdíl mezi např. .Write(' ') vs. .Write(" ") ♦ pozor na slovo = slovo + znak → lepší System.Text.StringBuilder ♦ StringBuilder .Clear() (.NET 4.0 - užívat s rozmyslem) ↔ .Length = 0 (.NET 1.1) ♦
čtení binárních souborů: FileStream.ReadByte/WriteByte + FileStream.Read/Write(byte[], ...)
System.Dignostics.Debug.Print, výhody, pozor na rychlost v Debug režimu, Output panel ve VS) ♦ unit testy (zběžný přehled, proč, jak) ♦ podpora pro unit testy ve Visual Studiu 2008/2010 (min. Professional): [TestClass], [TestMethod] • třída Assert a metody .AreEqual, apod. • [ExpectedException(typeof(ExceptionClassName)) • spouštění testů a jejich výsledky (debug trace ← Debug.Print) • v Test Projectu jeho Properties→Add→Unit test • testování interních tříd ([InternalsVisibleTo(...)]) a privátních metod (vygenerovaný ..._Accessor se zpublikovanými metodami) ♦
profiling (CPU sampling vs. instrumentation) • zběžně profiler ve VS 2010 (min. verze Premium) ♦ řešení Huffman I - na objektový návrh s rozmyslem (zase to nepřehánět, když to nemá smysl, např.: abstract class Uzel, class VnitrniUzel : Uzel, class List : Uzel zrovna zde není vhodné) • třídy Queue<T> (cyklická fronta v poli) a Stack<T>, parametr capacity ♦ Array.Sort(T[] array) a Array.Sort(Key[], Value[]) - pozor QuickSort → nestabilní třídění (podobně na kolekcích) ♦ BitConverter.GetBytes(int/long/...) → byte[] • BitConverter.ToInt32/ToInt64/... • BitConverter.IsLittleEndian ♦ BinaryReader/BinaryWriter - .NET/Mono implementace neřeší pomocí BitConverteru, tj. bez alokace pole bajtů ♦
little endian vs. big endian ♦ pozor zápis dat "na papír" typicky zleva doprava, zápis čísel zprava doleva ♦ Stream.WriteByte(byte)/Write(byte[] buffer, int startIndex, int count) ♦ připomenutí >>, <<, & (clear), | (set), ^ (flip), ~ (not)
Array.Copy(), Array.Clear() ♦ cena pretypování (konverze mezi hodnotovými typy: uint↔byte vs. int↔double) ♦ při nejistotě, jak něco v jazyce funguje → C# Language Specification 4.0 • př. 1 >> 32 == 1, tj. 1 >> 32 != 0 jako u shr na x86 → "shifty fungují nějak zvláštně" → najdu sekci Shift operators v C# Language Specification: "When the type of x is int or uint, the shift count is given by the low-order five bits of count. In other words, the shift count is computed from count & 0x1F" → (x >> c)↔(x >> (c & 0x1F)) → (1 >> 32) == (1 >> 0) == 1 ♦
minimalizovat platnost lokálních proměnných
class BinaryFileStream : FileStream : Stream {} vs. obalení libovolného streamu class BinaryStream : Stream { Stream innerStream; } ♦ class vs. struct: výhody a nevýhody ♦ vyvolání výjimky je časově náročné, tj. pozor např. int.Parse() vs. int.TryParse() ♦ System.Collections(.ArrayList) vs. System.Collections.Generic(.List<T>)
Sheet.GetSheet("...").Eval().Save() pomocí např. public Sheet Eval() { ...; return this; } ♦ využití struktur v Excelu: struct Value { public int IntValue; public object Error; } ♦ operátor checked a použití checked(a + b) nebo checked { ... } → OverflowException ♦ public vs. interní třídy
Expression.ParsePrefixExpression. Čili přidání neznámého typu operátoru vyžaduje úpravu metody Expression.ParsePrefixExpression.Expression.ParsePrefixExpression. Jiný program, který by používal knihovnu vzniklou přeložením tohoto zdrojového souboru, by pouze postupně zavolal metodu Expression.RegisterExpressionType(IOperatorExpressionType/IValueExpressionType) pro factories všech svých nově definovaných operátorů a hodnot (přibližně někde jako ve své metodě Main)
Napište program, který dostane na prvním řádku standardního vstupu výraz zapsaný v preorder notaci, výraz vyhodnotí a výsledek vypíše na standardní výstup. Ve výrazu se mohou vyskytovat celá kladná čísla, která se vejdou do 32-bit. integeru se znaménkem (tj. menší než 231 = 2147483648), binární operátory +, -, * a / a operátor unárního minus ~. Operátory i čísla jsou odděleny mezerami. Výsledek vypište jako celé číslo se znaménkem v dekadickém formátu.
Při vyhodnocování výrazu používejte výhradně celočíselnou aritmetiku. Všechny mezivýsledky se vejdou do 32-bit. integeru se znaménkem. Pokud by v průběhu výpočtu došlo k přetečení (mezivýsledek se nevejde do intu se znaménkem), vypište jako výsledek řetězec "Overflow Error". Pokud dojde k dělení nulou vypište chybový řetězec "Divide Error". A konečně pokud zjistíte, že zápis výrazu je z jakéhokoli důvodu neplatný (objevují se v něm neznámé tokeny, nedodržuje preorder formát apod.), vypište chybovou hlášku "Format Error".
Vyhodnocované výrazy jsou relativně krátké, takže není potřeba optimalizovat řešení na výkon. Místo toho bude kladen zvláštní důraz na kvalitu objektového návrhu a znovupoužitelnost kódu. Předpokládejte, že váš program bude sloužit jako základ knihovny pro práci s výrazy, tj. uživatelé takové knihovny budou typicky na jednom vstupním výraze provádět mnoho různých operací (opakované vyhodnocování, zjednodušování částí výrazu, apod.). Za opravdu kvalitní řešení můžete jednak dostat bonusové body navíc a jednak se vám to může vyplatit při řešení dalších domácích úkolů.
Vstup: + ~ 1 3
Výstup: 2
Vstup: / + - 5 2 * 2 + 3 3 ~ 2
Výstup: -7
Vstup: - - 2000000000 2100000000 2100000000
Výstup: Overflow Error
Vstup: / 100 - + 10 10 20
Výstup: Divide Error
Vstup: + 1 2 3
Výstup: Format Error
Vstup: - 2000000000 4000000000
Výstup: Format Error
Poznámka: poslední příklad vrací Format Error, protože číslo 4000000000 nevyjde jako mezivýsledek, ale je přímo zapsané ve výrazu.
IOperatorFactory • objektový návrh a modularita kódu ♦
generické typy a generické metody • návrhový vzor Visitor • Visitor vs. virtuální metody • pozor není "silver bullet" - scénáře vhodného užití Visitoru • virtualní metody vs. rozhodnutí switchem
Dictionary<Type, int> jako asociativní pole pro mapování mezi typem operátoru a jeho prioritou. Všimněte si, že algoritmus minimálního uzávorkování implementovaný visitorem PriorityInfixPrinterVisitor nijak nezasahuje (nemění) do datové struktury popisujíci strom výrazu (tj. do implementace třídy Expression a jejich potomků).OperatorPriorityQueryVisitor ve třídě PriorityInfixPrinterVisitor.
Napište výrazovou kalkulačku, která dostává na standardní vstup instrukce a na standardní výstup vypisuje výsledky případně chybové hlášky. Na jednom řádku vstupu je vždy jediný příkaz (prázdné řádky ignorujte). Kalkulačka zpracovává příkazy jeden po druhém, přičemž nejprve dokončí jeden příkaz než začne načítat další. Kalkulačka končí svou práci pokud dojdou vstupní data (čtení řádku vrátí null) nebo narazí na řádek obsahující pouze řetězec "end". Kalkulačka rozeznává tyto příkazy:
"=", za kterým následuje právě jedna mezera a výraz v preorder formátu (viz níže). Kalkulačka si výraz načte a nadále bude pracovat pouze s ním. Pokud již dříve načetla jiný výraz, starý výraz zapomene a nahradí jej novým. Předchozí správně načtený výraz program zapomene i v případě, že při zpracování příkazu "=" došlo k chybě. "i" vyhodnotí naposledy načtený výraz celočíselně a výsledek (jedno celé číslo) vytiskne na jeden řádek výstupu."d" vyhodnotí naposledy načtený výraz v reálné aritmetice s dvojitou přesností (64 bitů) a výsledek vytiskne na pět desetinných míst na jeden řádek výstupu."p" vytiskne na samostatný řádek naposledy načtený výraz v infixové notaci s explicitním zvýrazněním priorit operátorů. Jinými slovy, každý operátor je uzavřen do samostatných kulatých závorek (t.j. i celý výraz je v kulatých závorkách)."P" vytiskne na samostatný řádek naposledy načtený výraz v infixové notaci s minimálním možným uzávorkováním. Uzávorkují se pouze ty operace, u kterých je potřeba zvýšit prioritu oproti standardním pravidlům vyhodnocování. Nezapomeňte, že operátor unárního mínus má nejvyšší prioritu, následují operátory násobení a dělení a nejnižší prioritu mají operátory sčítání a odčítání. Standardně také předpokládáme levou asociativitu, tedy že posloupnost operátorů se stejnou prioritou vyhodnocujeme zleva doprava.V načítaném výrazu (za znaménkem "=") se mohou vyskytovat celá kladná čísla, která se vejdou do 32-bit. integeru se znaménkem (tj. menší než 231 = 2147483648), binární operátory +, -, * a / a operátor unárního minus ~. Operátory i čísla jsou odděleny mezerami.
Při vyhodnocování výrazu v celočíselné aritmetice (příkaz "i") počítejte s tím, že všechny mezivýsledky se vejdou do 32-bit. integeru se znaménkem. Pokud by v průběhu výpočtu došlo k přetečení (mezivýsledek se nevejde do intu se znaménkem), vypište na samostatný řádek chybový řetězec "Overflow Error". Pokud dojde při výpočtu v celočíselné aritmetice k dělení nulou, vypište řetězec "Divide Error".
Vyhodnocování výrazu v reálné aritmetice probíhá podle standardních pravidel IEEE - tj. včetně práce s hodnotami nekonečno, "not a number", apod. Pokud je výsledkem výrazu nějaká taková hodnota, vypište ji ve standardní textové reprezentaci poskytované knihovnami .NET.
Pokud narazíte na chybně zadaný příkaz nebo zjistíte, že zápis výrazu je z jakéhokoli důvodu neplatný (objevují se v něm neznámé tokeny, nedodržuje preorder formát apod.), vypište na samostatný řádek chybovou hlášku "Format Error". Pokud se příkaz "i", "d", "p" nebo "P" objeví dříve, než je načten jakýkoli výraz, vypište chybovou hlášku "Expression Missing".
Při tisku výrazů používejte pro unární mínus stejný znak jako pro binární "-". Do výrazu nevkládejte žádné další znaky (ani mezery).
Pro čtení ze standardního vstupu a výpisech na standardní výstup počítejte s tím, že váš program bude automaticky v CodExu spuštěn s vhodným nastavením národního prostředí.
Poznámka:Při hodnocení bude kladen důraz na zvolený objektový návrh. Úloha je koncipována tak, abyste si vyzkoušeli návrhový vzor Visitor. Za korektní implementaci tohoto vzoru můžete dostat až 5 bonusových bodů.
"$>" jsou vstupní):
$> i
Expression Missing
$> p
Expression Missing
$> $#!%
Format Error
$> = + 2 3
$> i
5
$> d
5.00000
$> = / 5 2
$> i
2
$> d
2.50000
$> = + 4 * 2 3
$> p
(4+(2*3))
$> P
4+2*3
$> = + + 1 1 1
$> p
((1+1)+1)
$> P
1+1+1
$> = +
Format Error
$> i
Expression Missing
$> end
Testovací sadu vstupních a vzorových výstupních dat naleznete zde.
IEquatable<T> • Object.Equals vs. Object.ReferenceEquals • rozhraní IEqualityComparer<T> a jeho implementace EqualityComparer<T>.Default • implementovat IList<T>, metoda ICollection<T>.Add přidává položku na konec
IEnumerable.GetEnumerator i IEnumerable<T>.GetEnumerator • interní public struct Enumerator • rychlé mazání/kopírování pole pomocí Array.Clear/CopyTo • správně vyhazovat InvalidOperationException při modifikaci enumerovaného Deque - řešení např. položka int version inkrementovaná s každou modifikací + Enumerator průběžně kontroluje, že má stále stejnou verzi ♦
připomenutí konceptu vláken (tj. unikátní callstack a data na zásobníku [lokální proměnné, parametry metod] vs. globální data sdílená mezi vlákny [halda, statické proměnné]) • běh a plánování vláken • vlákno v .NET = instance třídy System.Threading.Thread • vytvoření vlákna (hlavní metoda vlákna může být statická i instanční) • základní stavy vlákna: unstarted, running, waiting, terminated • základní přechody mezi stavy vklákna: Thread.Start a Thread.Join • Thread.CurrentThread • pozor na ladící výpisy v multithreaded aplikacích: volání Console.Write*/Read* se serializují, tj. běží v jednom okamžiku v max. 1 vlákně
Interlocked a chování čtení/zápisu z/do volatile proměnných
volatile proměnných a operací třídy Interlocked.Timer