\


 Monday, 01 December 2008
Běh aplikace na Windows Mobile při vypnutém displeji a opětovné probuzení zařízení

htc-touch-pro Jedním z problémů, se kterými se vývojáři často potýkají, je, že PDA (přesněji 7Windows Mobile Professional a Classic zařízení  - zařízení s dotykovým displejem) na rozdíl od Smartphonu (Windows Mobile Standard) po uplynutí doby nastavené v ovládacích panelech přecházejí do stavu "Off" (přesněji do stavu "Suspended" dle oficiální terminologie, stav Off má pouze Smartphone - my ale budeme dále ve spotu používat termín "off") , v němž  je provádění kódu aplikace zcela "zmrazeno". Aplikace jsou "hibernovány" a můžeme je považovat za dočasné mrtvolky.

Občas potřebujeme, aby sice došlo k vypnutí displeje zařízení a my tak drasticky a navíc zbytečně neredukovali výdrž baterie, ale aby naše aplikace na pozadí stále běžela. To je první  požadavek. Dalším z častých požadavků je schopnost aplikace probudit celé zařízení ze stavu "off".

Jestliže nám jde pouze o probuzení aplikace po uplynutí námi nastaveného času či při nějaké události (připojení k síti, změna proxy apod.), je řešení jednoduché - v našem arzenálu bude hrát prim omnipotentní funkce CeSetUserNotificationEx.

Když chceme v naší aplikaci pracovat i při vypnutém displeji (např. odpočítáváme sekundy v aplikaci "minutka") a současně probudit zařízení poté, co uběhla nastavená doba, musíme se začít přátelit s dalšími mocnými, avšak ke škodě samotné platformy Windows Mobile mizerně dokumentovanými API.

Co musíme zajistit:

  1. Zařízení nesmí přejít do "off" módu, ale musíme jej ponechat ve speciálním stavu, kdy je vypnutý displej, ale aplikace, které potřebují běžet, nejsou "zmrazeny". Námi požadovaný speciální stav se na Windows Mobile nazývá "unattended mód". Z hlediska uživatele je při "unattended módu" zařízení vypnuto, ale naše aplikace, která si o tento mód požádala, pokračuje ve své činnosti.


    K tomu nám poslouží API PowerPolicyNotify.
    PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_ON).
    Funkci PowerPolicyNotify předáme v prvním argumentu požadavek na Unattended mód a ve druhém argumentu konstantu UNATTENDED_ON s hodnotou 1 - TRUE.

  2. V unattended módu chceme zařízení zcela probudit. Opět použijeme API PowerPolicyNotify.
    PowerPolicyNotify(PowerMode.AppButtonPressed, 0)
    Tentokrát ale předáme konstantu PowerMode.AppButtonPressed, která simuluje stisknutí tlačítka "On/Off" na zařízení. Druhý argument musí být 0, funkcí s ním  při tomto volání nepracuje.

  3. Jestliže již nepotřebujeme běžet v "unattended módu", měli bychom dovolit zařízení, aby naši aplikaci hibernovalo. Nejpozději při ukončení aplikace se tedy musíme vzdát "unattended módu". Použijeme opět API PowerPolicyNotify s prvním argumentem PowerMode.UnattendedMode a ve druhém argumentu předáme konstantu UNATTENDED_OFF s hodnotou 0 - FALSE.

    PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_OFF).
    Sice jsem si ověřil, že při ukončení procesu je "unattended mód" zrušen samotnými Windows, ale je vždy příjemné  pracovat s kódem, v němž nejsou obvyklé  "prasácké" zlozvyky v  managed kódu, a nenechat tedy v naší situaci uklízet jen Garbage Collector nebo správce procesů ve Windows.

V následujícím příkladu naleznete také tento zakomentovaný kód:

 

powerSettings = SetPowerRequirement("BKL1:", (int)DeviceState.FullOn, POWER_FORCE, IntPtr.Zero, 0);

                if (powerSettings == IntPtr.Zero)
                {
                    MessageBox.Show("Failed"); 
                }

K zapnutí displeje je použito API SetPowerRequirement. Toto API ale v "unattended" módu selhává - vždy mi na zařízeních, která mám doma, vrací false. Dále musíte znát zkratku zařízení, pro které chcete nastavit nový režim. Na většině zařízení je displej identifikován zkratkou BKL1: - abyste si ale byli jisti, musíte název najít v registrech - klíče pod HKLM\Drivers\Active\*. API PowerPolicyNotify je tedy pro naše účely mnohem vhodnější.

Pozor: Některá zařízení (pokud se dobře pamatuji, tak např. MIO) nemají od výrobce podporu "unattended" módu a aplikace je stejně vždy zmrazena.

Zde je slíbený příklad - na formuláři je pouze listbox, do kterého je každých 5 vteřin přidáno další číslo. Můžete si ověřit, že čísla přibývají i v "unattended" módu. Po uplynutích každých 75 vteřin (konstanta WAKE_UP_COUNTER  (15) * 5) je zařízení opakovaně probuzeno k "plnému vědomí", což se u něj projeví rozsvíceným displejem. :-)

V ovládacích panelech (záložka Systém, položka Napájení, ikona Upřesnit) nastavte vypnutí displeje i zařízení po uplynutí jedné minuty neaktivity, abyste si příklad užili. ;-)

 

 

using System.Runtime.InteropServices;
using System.Windows.Forms;
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;

namespace PowerPDAProject
{
    public delegate void MethodInvoker();
    public partial class Form1 : Form
    {
        
        public const int UNATTENDED_ON = 1;
        public const int POWER_FORCE = 0x1000;
        public const int UNATTENDED_OFF = 0;

        public const int  WAKE_UP_COUNTER = 15; 
        private int m_secondsCount;
        private IntPtr powerSettings;
        
        public enum PowerMode
        {
            ReevaluateStat = 0x0001,
            PowerChange = 0x0002,
            UnattendedMode = 0x0003,
            SuspendKeyOrPwrButtonPressed = 0x0004,
            SuspendKeyReleased = 0x0005,
            AppButtonPressed = 0x0006
        }

        [DllImport("CoreDll.dll")]
        public static extern int PowerPolicyNotify(PowerMode powerMode, int flags);


        public enum DeviceState : int
        {

            Unspecified = -1,
            FullOn = 0,
            LowOn,
            StandBy,
            Sleep,
            Off,
            Maximum

        }
               

            [DllImport("CoreDll.DLL")]
            public static extern IntPtr SetPowerRequirement(String pvDevice, int DeviceState, int DeviceFlags, IntPtr pvSystemState, int StateFlags);


            [DllImport("CoreDll.DLL")]
            public static extern uint ReleasePowerRequirement(IntPtr hPowerReq);

        



        public Form1()
        {
            
            InitializeComponent();
            m_secondsCount = 0;
        }

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bool result = (PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_ON) != 0);
            MessageBox.Show(result.ToString());
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            m_secondsCount++;
            listBox1.Invoke((MethodInvoker)(
                                            () => listBox1.Items.Add(m_secondsCount.ToString())));

            if (m_secondsCount % WAKE_UP_COUNTER == 0)
            {

                PowerPolicyNotify(PowerMode.AppButtonPressed, 0);
                //powerSettings = SetPowerRequirement("BKL1:", (int)DeviceState.FullOn, POWER_FORCE, IntPtr.Zero, 0);

                //if (powerSettings == IntPtr.Zero)
                //{
                //    MessageBox.Show("Failed"); 
                //}
            }
        }

        private void Form1_Closing(object sender, CancelEventArgs e)
        {
            timer1.Enabled = false;
            timer1.Dispose();
            PowerPolicyNotify(PowerMode.UnattendedMode, UNATTENDED_OFF);
            ReleasePowerRequirement(powerSettings);
            
        }
    }
}


Monday, 01 December 2008 18:23:53 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky


 Tuesday, 11 November 2008
Pozvánka na kurzy - nový kurz Pokročilé návrhové vzory a objektové principy 2

Rád bych Vás pozval na další běh kurzu Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1 a hlavně Vás chci seznámit se zcela novým kurzem Pokročilé návrhové vzory a objektové principy 2.

Kurz Pokročilé návrhové vzory a objektové principy 2 je volným pokračováním kurzu Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1. Pojetím kurzu Pokročilé návrhové vzory a objektové principy 2 jsem se snažil vyhovět účastníkům předchozího kurzu, kteří mi, volně parafrázováno, říkali: "Nejlepší jsou ty části, kde probíráme jeden příklad za druhým a kde říkáte  - takto to dělám já." Můj zákazník, můj pán (zvláště, jestliže se v záměrech zcela shodneme :-D) - nový kurz je prošpikován příklady, tipy, kódem, vzorovými aplikacemi. Budu se těšit na oponeturu mých postupů. ;-)

Datum konání kurzu:  9. 3. - 11. 3. 2009

Místo konání: Hotel VILLA Praha  Okrajní 1, 100 00, Praha 10

U hotelu VILLA je  možné parkovat, po celý den máme k dispozici wifi připojení.

Pro jistotu dodám, že na kurzu jsou samozřejmě po celý den teplé a studené nápoje a v ceně kurzu jsou obědy v hotelu.

Podrobné informace o kurzu a možnost přihlásit se na kurz

Výběr z ohlasů na předchozí kurz

Předpoklady pro absolvování kurzu:

  1. Znalost alespoň jednoho z rodiny "C" jazyků (C#, Java) - příklady na školení jsou v jazyce C#.
  2. Částečná znalost UML = neutečete zděšeni z kurzu, když zobrazím diagram tříd.
  3. Nenávist ke kariéře zručného klikače a zaškrtávače ve vizuálních návrhářích a "wizardech", co s velkou vášní vytváří jedno strhující uživatelské rozhraní pro číselníky za druhým.
  4. Vhodné, nikoliv však nutné, je i absolvovat nejdříve školení Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1.
Program kurzu
  • Layer Supertype pro další vrstvy aplikace – vrstva pro řízení procesů a business transakcí.
  • Deklarativní změny v logice procesů v nasazené aplikaci prováděné samotným uživatelem.
  • Evidence historie objektů - různé přístupy.
  • Vlastní správce historie pro .Net Framework a Javu.
  • Řešení konkurenčního přístupu k datům.
  • Optimistická konkurence - různé implementace.
  • Pesimistická konkurence - různé implementace.
  • Pesimistická konkurence - různé implementace.
  • Konkurence napříč objektovými modelem - "Coarse grained lock" - různé implementace.
  • Thread Specific Storage – vlastní řešení.
  • Modelovani uživatelem zadávaných výběrových podminek (např. uživatelem definované sestavy nad objednávkami) – můj „Conditions“ vzor.
  • Návrh a implementace netriviálního právového frameworku.
  • Různé způsoby vyhodnocování práv - změna logiky za běhu aplikace.
  • Kde všude se nám hodí myšlenky návrhového vzor Accounting - modelování business aplikací jako množiny souvisejících transakcí.
  • Návrhové vzory Query a Repository a jejich vazba na „Conditions“ vzor.
  • Různé přístupy k vytváření uživatelského rozhraní - Model-View-Controller, Model-View-Presenter, Passive View - můj vlastní Form Controller.
  • Aplikace založené na pluginech – vzorové přístupy a doporučení.
  • Správa "cizích" pluginů/služeb ve vlastních aplikacích.
  • Vzor Component Configurator - správa pluginů.
  • Vzor Interceptor - ukázky business aplikací, které jsou rozšiřovány za běhu aplikace s minimálním úsilím a bez strastí opakovaného nasazení aplikace.
  • Kdy použít vzor Special Case?
  • Remote Facade a Data Transfer Object - různé přístupy k distribuované aplikaci.
  • Vzory pro zpracování požadavků na aplikaci-službu.
  • Kódování vzoru Acceptor-Connector.
  • Asynchronous Completion Token - vlastní pomocné objekty pro zjednodušení asynchronních úloh.
  • Kódování vzoru Proactor.
  • Kódování vzoru Reactor.
  • Thread Safe Interface - co pro nás znamená v moderních prostředích (Java a .Net Framework)
  • Co jsou takzvané “Enterprise segmenty” v business aplikacích?
  • V průběhu celého kurzu - kompletní případová studie existující business aplikace, v níž jsou zakódovány postupy zmiňované na kurzu - dlouhá procházka kódem. :)

 

Kurz Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací 1

Datum konání kurzu:  20. 4. - 22. 4. 2009

Místo konání: Hotel VILLA Praha  Okrajní 1, 100 00, Praha 10

U hotelu VILLA je  možné parkovat, po celý den máme k dispozici wifi připojení.

Pro jistotu dodám, že na kurzu jsou samozřejmě po celý den teplé a studené nápoje a v ceně kurzu jsou obědy v hotelu.

Organizační informace ke kurzu

Program kurzu

Výběr z ohlasů na kurz



Tuesday, 11 November 2008 16:38:10 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Analytické drobky | Kurzy UML a OOP | Návrhové vzory | UML


 Friday, 07 November 2008
Změna nastavení aplikace pro stahování TV programu

Po  opakovaných dotazech v mailu, proč se posledních 14 dní nestahuje TV program automaticky do MDA, dávám odpověď sem, abych nemusel všem odpovídat individuálně.

Pravděpodobně se opět trochu změnilo generování TV programu a je nutné upravit kofiguraci aplikace.

V konfiguračním souboru RStein.SimpleWMDownloader.exe.config se ujistěte, že máte nastaveny následující klíče takto:

<add key="StartDate" value="20081104"/>
<add key="StartDateValue" value="1225753200"/>
<add key="DayOffsetValue" value="86400"/>

 

Vzorový konfigurační soubor může vypadat takto:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="StartDate" value="20081104"/>
        <add key="DayQueryKey" value="dny[]"/>
        <add key="StartDateValue" value="1225753200"/>
        <add key="DayOffsetValue" value="86400"/>
        <add key="NumberOfDays" value="8"/>
      </appSettings>
            <system.diagnostics>
            <trace autoflush="true" indentsize="0">
                <listeners>
                    <add name="myListener"
                      type="System.Diagnostics.TextWriterTraceListener"
                      initializeData="c:\Rstein.SimpleWMDownloader.log" />
                </listeners>
                </trace>
     </system.diagnostics>
</configuration>

 

Podrobné informace o aplikaci a instalace

 

A na okraj dodám, že aplikaci nemusíte použivat jen pro stahování TV programu, ale lze s ní stáhnout jakoukoli stránku na webu, kterou chcete při synchronizaci přes AS uložit do (M|P)DA. Stačí do souboru DownloadConfig.txt přidat další řádky v tomto tvaru:

URL;cesta k souboru na pda

Příklad:

http://www.example.com/vzor.txt;\Storage card\example\vzor.txt
http://www.example.com/dalsi2.txt;\Storage card\example\dalsi2.txt



Friday, 07 November 2008 11:29:27 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | Compact .Net Framework | Mobilitky | Ostatní


 Tuesday, 14 October 2008
Prezentace o "netradičních" návrhových vzorech pro CZ JUG ke stažení

Pro CZJUG jsem přednášel o "netradičních" vzorech - myšleno méně známých  vzorech a možná málo známých aspektech provařených vzorů. Tímto ještě jednou děkuji Dagimu a lidem ze SUNu za pozvání  na příjemnou akci i za jejich tolerantní přístup k mému, s nadsázkou,  mluvení o provaze (=C#, Visual Studio) v domě oběšencově (respektive z pohledu konkurence určitě vhodného kandidáta na business oběšence). :-)

Prezentaci si můžete stáhnout v ppt formátu nebo v pdf přímo na stránkách CZJUG.



Tuesday, 14 October 2008 19:18:42 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Analytické drobky | Návrhové vzory | UML


 Sunday, 05 October 2008
Adaptéry pro funktory v C++ => Adaptéry pro funkce v C#

V C++ je snadné napsat takzvané adaptéry pro funkce, respektive pro funktory - objekty, chovající se jako funkce. K čemu jsou adaptéry dobré? Představme si, že máme napsanou funkci equal_to, která přijímá dva argumenty a vrátí true, jestliže jsou oba argumenty shodné, jinak vrátí false. Jedná se tedy o binární funktor, protože přijímá dva argumenty. Nyní potřebujeme pomocí stl metody find_if vyhledat v naší kolekci všechny prvky, jejichž hodnota je rovna 10. Podmínku v metodě find_if musí představovat unární funktor (funktor přijímající jeden argument - prvek v kolekci - a vracející true jen v případě, že prvek v kolekci podmínku splňuje). Je zřejmé, že binární funktor nemůžeme použít na místě, kde je očekáván unární funktor. V C++ můžeme ale v této situaci namísto psaní dalšího unárního jednoúčelového funktoru využít speciálního adaptéru, jehož účelem je konverze binárního funktoru na unární. Adaptér, který přijde vhod pro naše účely, se jmenuje binder1st (zde by bylo možné použít i adaptér binder2nd). Adaptér binder1st očekává, že mu předáte binární funktor, který má být převeden na unární  a hodnotu, která má být vždy použita jako první argument (proto ...1st) při volání binárního funktoru. Adaptér binder2nd se od adaptéru binder1st liší jen tím, že předaná hodnota bude použita vždy jako druhý argument předaného binárního funktoru. Jinými slovy - při volání funktoru binder1st je kolekce spokojena, že dostala unární funktor, ale náš funktor binder1st interně deleguje volání na binární funktor, kterému předá jako první argument hodnotu, kterou jsme zadali při vytvoření adaptéru binder1st, a jako druhý argument objekt z kolekce, na kterém se má otestovat platnost podmínky.

binder1st<equal_to<int> > equalPredicate = bind1st(equal_to<int>(), 10);
iterator it1 = find_if(v1.begin(), v1.end(), equalPredicate);

V předchozím kódu jsme vytvořili adaptér (unární funktor) nazvaný equalPredicate, který zprostředkovává přístup k binárnímu funktoru equal_to. Skutečnost, že je  funktor equal_to binárním funktorem, poznáme z jeho deklarace.

template<class Type>
   struct equal_to : public binary_function<Type, Type, bool> 
   {
      bool operator()(
         const Type& _Left, 
         const Type& _Right
      ) const;
   };

Na druhém řádku  příkladu adaptér equalPredicate předáme funkci find_if, která porovná každý element v kolekci (const Type&  _Right)  s hodnotou 10. Funkce vrátí první prvek, který vyhoví podmínce _Left==Right  (konkrétně v našem případě jde o podmínku  10 == PrvekVKolekci). Konstantní hodnota 10 byla předána funkci bind1st a bude  představovat při každém volání "adaptovaného" funktoru equal_to  adaptérem equalPredicate  hodnotu argumentu _Left operátoru(). Funkce bind1st je "syntaktickým cukrem", který zjednodušuje vytváření adaptéru, protože nemusíme specifikovat všechny typové parametry adaptéru binder1st, ale spolehneme se na typovou inferenci provedenou kompilátorem.

Konec rychlé exkurze do C++. I v C# nám mohou adaptéry pro delegáty přijít vhod. Představme si, že již máme napsanou třídu, která vrací výsledek porovnání dvou hodnot ("je menší než", "je větší než").

    static class ComparerEx
    {
        public static bool GreaterThan<T>(T a, T b)
        {
            return Comparer<T>.Default.Compare(a, b) > 0;
        }

        public static bool LessThan<T>(T a, T b)
        {
            return Comparer<T>.Default.Compare(a, b) < 0;
        }
    }

Funkce chceme použít v LINQ podmínkách (např. můžeme chtít z kolekce celých čísel vrátit jen všechna čísla, jež jsou větší než 10). Ale také můžeme chtít sadu podmínek, které můžeme libovolně kombinovat a skládat tak jednoduše výrazy typu "všechny hodnoty z kolekce, jež jsou větší než 20, ale menší než 90". Stejně tak můžeme chtít za chvíli podmínku znegovat a máme zájem o hodnoty nepatřící do intervalu 20-90. Namísto psaní "jednoúčelových" (i anonymních) metod si můžeme jednotlivé podmínky předpřipravit a pomocí adaptérů pro delegáty je skládat do složitějších podmínek. Také můžeme chtít stejnou podmínku použít při restrikci v LINQu (Where extenze pracující s delegátem typu Func<  >) i při práci se staršími metodami (např. FindAll u List<T>), které očekávají odkaz na delegáta typu Predicate. To vše nám speciální adaptéry pro delegáty v C# umožní.

Nejprve se podívejme na použití adaptérů.

 

class Program
    {
        static void Main(string[] args)
        {

            
            Random rand = new Random();

            //Vygenerování náhodných čísel v rozsahu 1..100
            List<int> myList = new List<int>(Enumerable.Range(1, 100).Select((i) => rand.Next(1, 100)));

            //Vytvoření predikátu pro where část LINQ dotazu (všechna čísla, kromě čísel v rozsahu 10 - 90
            var predicate = FuncExtension.Bind2nd<int, int, bool>(10, ComparerEx.GreaterThan);
            predicate = FuncExtension.And(predicate, FuncExtension.Bind2nd<int, int, bool>(90, ComparerEx.LessThan));
            predicate = FuncExtension.Not(predicate);
            
            //LINQ dotaz  - v selectu je do anonymního typu vyzvednut i index prvku v kolekci
            var result = myList
                                .Where(predicate)
                                .Select((elem, index) => new {elem, index});
                         

            //Výpis LINQ dotazu
            foreach (var res in result)
            {
                
                Console.WriteLine("{0}:{1}", res.index, res.elem);
                
            }

            //Ukázka konverze podmínky (Func<?, bool> na delegáta typu Predicate očekávaného funkcí FindAll
            var vals = myList.FindAll(FuncExtension.ToPredicate(predicate));
            
            //Musíme dostat stejné výsledky jako v předchozím dotazu s využitím LINQu
            foreach (var val in vals)
            {
                Console.WriteLine(val);
            }

            Console.ReadLine();
        }
    }

V příkladu jsme si naplnili myList náhodnými čísly v intervalu od 1 do 100. Proměnná predicate představuje podmínku.

Použitím adaptéru Bind2nd(FuncExtension.Bind2nd<int, int, bool>(10, ComparerEx.GreaterThan);) vytvoříme podmínku "všechna čísla větší než  10". Vidíme, že jsme funkci ComparerEx.GreaterThan, která očekává dva argumenty, "adaptovali-převedli" na funkci (přesněji řečeno na delegáta), který očekává jeden argument. Druhým argumentem funkce ComparerEx.GreaterThan je vždy konstantní hodnota 10 předaná  při volání funkce Bind2nd.

V dalším kroku vytvoříme podmínku ("všechna čísla menší než 90" - FuncExtension.Bind2nd<int, int, bool>(90, ComparerEx.LessThan)); ) a zkombinujeme ji s předchozí podmínkou pomocí speciálního adaptéru, který představuje operátor And (FuncExtension.And(predicate, FuncExtension.Bind2nd<int, int, bool>(90, ComparerEx.LessThan))). Operátor And je pro zbytek aplikace stále jen obyčejným (unární) delegátem na funkci, která přijímá jeden argument a vrací true nebo false. Nyní máme tedy podmínku "všechna čísla větší než 10 a menší než 90".

Naše konečná podmínka ale má mít podobu (""všechna čísla s výjimkou čísel větších než 10 a menších než 90"). Proto použijeme další speciální adaptér Not, který v předchozích krocích sestavenou podmínku zneguje (FuncExtension.Not(predicate);)

Z kolekce myList vybereme přes LINQ všechna čísla splňující podmínku (proměnná predicate s definicí podmínky je argumentem extenzní metody Where) a vypíšeme je do konzole.

Nakonec ještě stejnou podmínku chceme předat metodě FindAll. Metoda FindAll ale očekává delegáta nazvaného Predicate, a proto použijeme další "adaptující" funkci ToPredicate, který stávající definici podmínky konvertuje na Predicate.

Jak adaptéry pracují? Podívejme se na funkci Bind2nd.

 public static Func<T0, R> Bind2nd<T0, T1, R>(T1 bindValue, Func<T0, T1, R> originalFunc)
        {
            return (arg => originalFunc(arg, bindValue));
        }

Bind2nd je generická funkce, která jako argument (T1 bindValue) očekává hodnotu, která bude představovat vždy druhý argument adaptovaného delegáta (Func<T0, T1, R> originalFunc - funkce přijímající dva argumenty, první typu T0, druhý typu T1 a vracející R). Funkce vrátí nového delegáta (Func<T0, R>), který ukazuje na funkci očekávající jeden argument  typu  T0 a vracející instanci generického typu R. Delegát při svém spuštění pouze vezme předaný argument (arg) a poskytne jej jako první argument delegátovi originalFunc, kterému současně vždy předá jako druhý argument hodnotu v původním argumentu bindValue.

Podobně fungují i další adaptéry. Pro zajímavost se podívejme na adaptér ToPredicate, který z předaného delegáta vytvoří delegáta typu Predicate, čehož jsme využili v předchozím příkladu.

public static Predicate<T> ToPredicate<T>(Func<T, bool> originalFunc)
        {
            return arg => originalFunc(arg);
        }

Funkce očekává ve svém argumentu originalFunc odkaz na delegáta typu Func, který přijímá jeden argument typu T a vrací bool. My vrátíme delegáta typu Predicate<T>, přičemž vrácený lambda výraz deleguje vykonání funkce na původního delegáta originalFunc. Pro zbytek aplikace je delegát Func<T, bool> skryt za rozhraním adaptéru Predicate<T>, který nám pomohl pro funkci FindAll přeložit podmínku "v neznámém jazyce" do srozumitelné řeči.

Následuje kompletní výpis kódu adaptérů. To, co nám prozatím chybí, je ekvivalent funkce bind1st (bind2nd) z C++, který by nám zjednodušil zápis podmínek bez nutnosti zadávat "ručně" generické argumenty. Ale o tom popřemýšlím zase "někdy jindy". :-)

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FunctionExtensions
{
    static class FuncExtension
    {
        
        public static Func<T1, R> Bind1St<T0, T1, R>(T0 bindValue, Func<T0, T1, R> originalFunc)
        {
            return (arg => originalFunc(bindValue, arg));
        }

        public static Func<T0, R> Bind2nd<T0, T1, R>(T1 bindValue, Func<T0, T1, R> originalFunc)
        {
            return (arg => originalFunc(arg, bindValue));
        }

        
        public static Func<T0, bool> Not<T0>(Func<T0, bool> originalFunc)
        {
            return (arg => !originalFunc(arg));
        }

        public static Func<T0, T1, bool> Not<T0, T1>(Func<T0, T1, bool> originalFunc)
        {
            return ((arg1, arg2) => !originalFunc(arg1, arg2));
        }

        public static Func<T0, T1, T2, bool> Not<T0, T1, T2>(Func<T0, T1, T2, bool> originalFunc)
        {
            return ((arg1, arg2, arg3) => !originalFunc(arg1, arg2, arg3));
        }

        public static Func<T0, T1, bool> And<T0, T1>(Func<T0, T1, bool> originalFunc, Func<T0, T1, bool> originalFunc2)
        {
            return ((arg1, arg2) => originalFunc(arg1, arg2) && originalFunc(arg1, arg2));
        }

        public static Func<T0, bool> And<T0>(Func<T0, bool> originalFunc, Func<T0, bool> originalFunc2)
        {
            return (arg1  => originalFunc(arg1) && originalFunc2(arg1));
        }

        public static Func<T0, T1, bool> Or<T0, T1>(Func<T0, T1, bool> originalFunc, Func<T0, T1, bool> originalFunc2)
        {
            return ((arg1, arg2) => originalFunc(arg1, arg2)  || originalFunc(arg1, arg2));
        }
        
        public static Func<T0, bool> Or<T0>(Func<T0, bool> originalFunc, Func<T0, bool> originalFunc2)
        {
            return (arg1 => originalFunc(arg1) || originalFunc2(arg1));
        }

        public static Predicate<T> ToPredicate<T>(Func<T, bool> originalFunc)
        {
            return arg => originalFunc(arg);
        }
    }
}


Sunday, 05 October 2008 17:34:45 (Central Europe Standard Time, UTC+01:00)       
Comments [4]  .NET Framework | Compact .Net Framework | LINQ | Návrhové vzory


 Monday, 15 September 2008
Několik poznámek k přetypovávání generických kolekcí

Při práci s generickými kolekcemi asi každy občas zatouží převést generickou kolekci s objekty typu B na generickou kolekci s objekty typu A, přičemž instinktivně očekává, že když je typ A předkem typu B, žádný problém při konverzi nenastane a navíc půjde o konverzi implicitní - automatickou. Instinkty, intuice a další feminní rysy jsou ale při programování spíš zátěží (že by jeden z hlavních důvodů, proč je stále tak málo programátorek? ) :-) )

Konkrétně - mějme tyto dvě třídy.

 

    class Test
    {

        public override string ToString()
        {
            return "Test";
        }
    }


    class SpecialTest : Test
    {
        public override string ToString()
        {
            return "SpecialTest";
        }
    }
    
Vytvoříme si kolekci (List) odvozených tříd SpecialTest.
List<SpecialTest> srcList = new List<SpecialTest> { new SpecialTest(), new SpecialTest() };

Při pokusu přetypovat List<SpecialTest> na kolekci objektů typu Test (List<Test>)  neuspějeme.

List<Test> invalidAttemptList = srcList;

Cannot implicitly convert type 'System.Collections.Generic.List<CollectionInheritance.SpecialTest>' to 'System.Collections.Generic.List<CollectionInheritance.Test>'). Důvod je zřejmý - dva generické objekty List, jeden s generickým argumentem Test a druhý s generickým argumentem SpecialTest, jsou dvě zcela rozdílné a nezávislé třídy a skutečnost, že třída SpecialTest je potomkem třídy Test, neznamená, že by stejný vztah platil mezi třídami List<SpecialTest> a List<Test>. Autoři jazyka C# (dle svých vyjádření prozatím?) zavedli toto omezení kvůli typové bezpečnosti.

Možností, jak konverzi provést, je mnoho. Vyjmenujme alespoň ty, které nově přinesl LINQ.

LINQ nám nabízí pro daný účel metodu Cast, která zkonvertuje prvky ze zdrojové kolekce (IEnumerable) na (generický) typ předaný metodě.

IEnumerable<Test> ieList = srcList.Cast<Test>();

Jestliže by nebylo možné všechny prvky v kolekci převést na 'Test', bude vyvolána výjimka. Když máme v kolekci směs objektů z různých tříd nebo podporujících různá rozhraní, můžeme přetypovat pomocí dalšího standardního LINQ operátoru OfType, který do výsledné kolekce vloží jen ty objekty, které se pomocí operátoru as podaří přetypovat na cílový typ. Objekty, které přetypovat na cílový typ (v následujícím kódu tedy na  typ Test) nelze, jsou ignorovány.

IEnumerable<Test> ieList2 = srcList.OfType<Test>();

Jestliže nechceme mít jako výslednou kolekci typ IEnumerable, můžeme použít další LINQ operátor ToList() a výsledek přetypování nám bude vrácen v instanci List<T>.

List<Test> testList = srcList.OfType<Test>().ToList();

Alternativou k předchozímu zápisu může být využití konstruktoru třídy List.

List<Test> testList2 = new List<Test>(srcList.OfType<Test>());

 

Jestliže nechcete vždy pracovat jen s kolekcí typu List a chcete přetypovávat například na typové kolekce, využijete mé extenzní metody.

 

Collection<Test> trgList = srcList.WideningConvert<SpecialTest, Test, Collection<Test>>();

Metodě WideningConvert předáte jako typové argumenty aktuální typ v generické kolekci (SpecialTest), cílový-výsledný typ, na který má být zdrojový typ převeden (Test) a typ kolekce, která má být vrácena. Kolekce musí mít bezparametrický konstruktor a také musí podporovat rozhraní ICollection<T>.

Pomocí extenzní metody lze přetypovávat i z kolekce objektů typu "předek" na kolekci "potomků".

  

Collection<SpecialTest> nextList = trgList.NarrowingConvert<Test, SpecialTest, Collection<SpecialTest>>();
 
 

Metoda WideningConvert svým názvem dává najevo, že je určena pro implicitní ("bezpečnou") konverzi, kdy převádíte kolekci potomků na kolekci předků. Obdobně, metoda NarrowingConvert přetypovává kolekci objektů typu "předek" na kolekci objektů typ "potomek (explicitni konverze). Metoda NarrowingConvert se pokusí převést každý objekt ve zdrojové kolekci na cílový typ ("Potomek") pomocí operátoru as. Jestliže přetypování selže, je zdrojový objekt ignorován, a proto může výsledná kolekce vrácená metodou NarrowingConvert obsahovat méně prvků než kolekce zdrojová.

 

Zde je kompletní výpis metod. Bylo by samozřejmě možné začít uvažovat nad zjednodušením kódu pro přetypovávání, ale to si necháme "napříště".

 

    public static class ConvertExtensions
    {
        /// <summary>
        /// Metoda převede kolekci - metoda je určena pro přetypování generické kolekce s "potomky" na generickou kolekci s "předkem"
        /// </summary>
        /// <typeparam name="T0">Typ elementu v zdrojové kolekci</typeparam>
        /// <typeparam name="P">Typ elementu v cílové kolekci</typeparam>
        /// <typeparam name="R">Typ cílové kolekce</typeparam>
        /// <param name="source">Zdrojová kolekce</param>
        /// <returns>Cílovou kolekci s převedenými elementy</returns>
        public static R WideningConvert<T0, P, R>(this IEnumerable<T0> source) 
                                             where R : ICollection<P>, new()
                                             where T0: P            
        {

            if (source == null)
            {
                throw new ArgumentNullException();
            }
            R retCol = new R();

            foreach (T0 srcElem in source)
            {
                retCol.Add(srcElem);
            }
            return retCol;
        }

        /// <summary>
        /// Metoda převede kolekci - metoda je určena pro přetypování generické kolekce s "předkem" na generickou kolekci "potomků"
        /// </summary>
        /// <typeparam name="T0">Typ elementu v zdrojové kolekci</typeparam>
        /// <typeparam name="P">Typ elementu v cílové kolekci</typeparam>
        /// <typeparam name="R">Typ cílové kolekce</typeparam>
        /// <param name="source">Zdrojová kolekce</param>
        /// <returns>Cílovou kolekci s převedenými elementy</returns>
        ///<remarks>Počet prvků v cílové kolekcí může být menší než počet prvků v zdrojové kolekci, protože veškeré objekty, které nelze zkonvertovat na <typeparamref name="R"/>, jsou ignorovány</remarks>
        public static R NarrowingConvert<T0, P, R>(this IEnumerable<T0> source)
                                                  where R : ICollection<P>, new()
                                                  where P : class, T0
        {

            if (source == null)
            {
                throw new ArgumentNullException();
            }
            R retCol = new R();

            foreach (T0 srcElem in source)
            {

                P retValue = srcElem as P;

                if (retValue != null)
                {
                    retCol.Add(retValue);
                }
            }
            return retCol;
        }
      
                                             
    }


Monday, 15 September 2008 13:29:48 (Central Europe Standard Time, UTC+01:00)       
Comments [4]  .NET Framework | Compact .Net Framework | LINQ


 Monday, 08 September 2008
Poslední volná místa na kurzu OOP a návrhových vzorů

Několik zájemců o veřejný kurz OOP a návrhových vzorů raději zvolilo inhouse variantu kurzu, proto bych vás rád upozornil,  že se můžete ještě nyní dodatečně hlásit na kurz OOP a návrhových vzorů.

Pozvánka s odkazy na podrobné informace o kurzu.



Monday, 08 September 2008 08:58:28 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Analytické drobky | Kurzy UML a OOP | Návrhové vzory | Ostatní


 Monday, 11 August 2008
Dobře utajený nástroj - Interní Spy

Dnes jsem náhodou našel užitečný  "Easter Egg" skrytý ve Windows Mobile (určitě ve verzích 5 a 6). Když držíte tlačítko "Action" a současně přidržíte stylus  - jako kdybyste chtěli zobrazit kontextové menu - v horní liště s notifikačními ikonami přesně mezi popiskem formuláře a první notifikační ikonou, zobrazí se okno Internal Use Spy, ve kterém najdete informace o hwnd aktivního okna, třídě (class), pozici - než bych se rozepisoval, podívejte se sami na obrázky, jaké informace o okně jsou zobrazeny. Docela užitečná věc, když  nemám po ruce notebook s nástrojem Remote Spy.

 

SPY1 SPY2



Monday, 11 August 2008 14:57:08 (Central Europe Standard Time, UTC+01:00)       
Comments [1]  Compact .Net Framework | Mobilitky


 Wednesday, 06 August 2008
Recenze: Effective C++ 55 Specific Ways to Improve Your Programs

Effective C  Při nákupu na Amazonu jsem neodolal a do košíku jsem vložil už třetí edici netradičně psané knihy  Effective C++ od Scotta Meyerse. Konvenčnímu stylu průměrných odborných knížek se Effective C++ se vymyká už jen tím, že nejde o další popularizující a tisíckrát opakovanou snůšku jednoduchých rad a receptů psanou za účelem "za 21 dní či hodin uděláme z naprostého technického idiota sebevědomého technického idiota, jehož mozek dokáže na pohovoru nějakému jinému (bez) mozku z personální agentury úspěšně předstírat znalost nějaké technologie". Scott Meyers se nesnaží ani předstírat, že se z jeho knihy naučíte základy C++, ale předpokládá, že jste schopni v C++ programovat a že jste si již stačili uvědomit  temná zákoutí  jazyka C++, nebo jste v nich v horším případě na některém projektu začali i bloudit. Kniha Effective C++ je pobídkou, abyste si osvojili 55 postupů z různých výrazových prostředků jazyka C++, které vám umožní temná zákoutí jazyka C++ obcházet. Pro vývojáře, jejichž "mateřským" jazykem je C# nebo JAVA, může být zajímavé, že ve třetí edici Scott asi na základě svých špatných zkušeností na vícero místech v knize opakuje: "Takto je to možná v Javě, možná vám to nevadí ani v C#, ale zapamatujte si, že C++ není JAVA ani C#." Pořád lepší být mentorován a poučován od člověka, který svoje varování vyřkne proto, že se pravděpodobně při svých konzultacích setkal s odstrašujícími ukázkami kódu, který byl zcela bezmyšlenkovitě převeden ze C# do C++, než být vlečen projektovým osudem vtěleným do Ganttových diagramů , který umí být mnohem nemilosrdnější než běžný lidský osud a donutí ve svých dosavadních garbage collectorem stvrzených jistotách spokojeně si hovící vývojáře zabývat se C++ bez možnosti utéct zpět do závětří "managed" kódu. :-)

V úvodu Scott popíše, jaké termíny a pojmy by měl každý vývojář v C++ znát a hned v prvním článku popíše své vidění jazyka C++. Jazyk C++ je pro něj spíše federaci poměrně samostatných jazyků, než jedním klasicky strukturovaným programovacím jaykem. C++ vám dovoluje psát programy v klasickém "C" stylu, můžete pracovat s třídami, dědičností a virtuálními metodami stejně jako v každém jiném jazyce podporujícím OOP. Také ale se můžete nechat zlákat šablonami a metaprogramováním a využít toho, že na první pohled ničím nevynikající a dříve některými jen za chytřejší preprocesor  považované šablony jsou "turingovsky" úplným jazykem. V knize je za autonomní jazyk považována i knihovna STL, i když autor vysvětluje, že za dalšího člena C++ federace ji považuje hlavně proto, že přináší další programové konvence a idiomy (funktory, "chytré" pointery", iterátory, ...), které musíte bezpodminečně ovládat , chcete-li použivat další knihovny třetích stran, jež jsou na těchto standardních technikách a postupech často založeny.

V dalších článcích se Scott zabývá nutností inicializovat všechny proměnné. Při zdůrazňování důležitosti konstantních členů třídy pookřejete, že konečně nějaký autor pořádně vysvětlil, jak správně použít klíčové slova mutable, ale možná nebudete spokojeni s motivací pro dvojí přetypovávání (const_cast) u konstatních metod třídy (například indexer - operator[]), které se odkazují na své nekonstantní protějšky, protože v nich nechcete duplikovat kód.

Dalším velkým tématem jsou konstruktory, destruktory a operátory přiřazení. V článcích se dozvíte, jak neopakovat typické chyby při psaní kopírovacích konstruktorů nebo operátorů přiřazení, jak zajistit, že při vytváření hluboké kopie objektu inicializujete i zděděné členy, nebo proč je pro vaši aplikaci smrtící, když z destruktoru objektu necháte vybublat výjimku. I u těchto nepříliš zajímavých témat oceníte Scottův živý a vášní pro své téma prodchnutý jazyk. Když upozorňuje na to, v jakých případech vývojáře kompilátor nebude nijak varovat, že kopírovací konstruktory a operátory přiřazení nefungují správně, sarkasticky k tomu dodá, že jde pravděpodobně o odplatu za to, že už nenecháte kompilátor, aby si sám vyhrál s deklarací kopírovacích konstruktorů a operátorů a přiřazení. Když popisuje, jak sdílet kód mezi vícero konstruktory, místo zabíhání do nesmyslných podrobností debatu rázně utne tvrzením, že se sice můžete zkusit odkázat z jednoho konstruktoru na jiný konstruktor a že na první pohled mohou vaše konstrukce vypadat pěkně, ale že stejně jde o nesmysl a jazyk C++ nic takového nedovoluje. Chcete-li sdílet kód v konstruktorech, dostanete praktickou radu, ke které byste asi sami stejně dospěli, abyste společný kód vyčlenili do samostatné privátní "init" metody. 

V dalších částech se Scott zabývá chytrými pointery. Celá kapitola 4 s články 18 až 25 je vyhrazena návrhu správného rozhraní třídy. Principy jako "navrhujte rozhraní tak, aby jej bylo jednoduché použít, ale velmi komplikované zneužít" asi v nikom nevyvolají nevoli nebo chuť polemizovat. S článkem preferujte nečlenské a non-přátelské (non-friend) funkce nad členskými už asi ale každý nebude souhlasit, i když Scottovy argumenty nejsou špatné. Mně je ale bližší C# a další jazyky, které vás nutí umístit každou funkci do těla třídy. V C++  uplatňuji pouze příslovečnou výjimku z pravidla u operátorů a funkcí, u nichž vyžaduji, aby k implicitní konverzi argumentů mohlo dojit u všech argumentů, což je téma, které i Scott pěkně popisuje v článku 25 nazvaném "Declare non member functions when type conversions should apply to all parameters".

Slyšeli-li jste někdy letmo o PIMPL idiomu a divili jste se, k čemu by vůbec byl dobrý, nebo vám vadila u projektu dlouhotrvající a nutná rekompilace po změně jednoho hlavičkového souboru z nějaké knihovny? V knize se dozvíte, jak "kompilační" závislosti omezit právě s využitím do všech podrobností dokonale vysvětleného PIMPL postupu. Kdo je stále zajatcem bludu, že dědičnost a virtuální metody jsou jediným všemocným kladivem na všechny problémy, měl by si pořádně pročíst kapitolu zabývající se dědičností, ve které Scott zmiňuje, jak poznat a implementovat vztah "je podtypem" (veřejná dědičnost"), nebo "využiji nějaké služby jiné třídy" (kompozice, privátní dědičnost). Také fandové vícenásobné dědičnosti by si měli podrobně přečíst, proč Scott její použití nijak nezakazuje, ale jemně v názvu článku 40 varuje, že by měli být soudní. Protože jinak při šíleném křížení tříd můžete velmi rychle  o rozum a logický úsudek přijít, dodal bych já. A možná rádi, abyste nemohli už dnes přesně usuzovat, kolik ještě stačíte na dohodnuté podpoře aplikace za několik let prodělat. :)

Šablony jsou tématem kapitoly 7, ale výklad není veden příliš do hloubky. Scott se spíše soustřeďuje na řešení klasických "špeků", jimž se při použití šablon nevyhnul asi nikdo. Podědili jste z šablonové třídy, voláte metodu z bázové třídy a kompilátor protestuje, Scott ukazuje, jak "chybu" odstranit - ať už použitím klíčového slova this (this->MetodaZBazoveTridy();), instruováním překladače o existenci metody v bázové třídě za pomoci klíčového slova using (using BazovaTrida<Typ>::MojeMetoda;) nebo uvedením jména bázové třídy před jménem metody při volání (BazovaTrida<Typ>::MojeMetoda();). Sympatické je, že nezůstane u prostého výčtu možných řešení problému, ale Scott upozorní, v čem tkví nedostatky jednotlivých řešení, takže u poslední varianty se vám ihned dostane varování, že jde o nejméně vhodné řešení, protože bude-li metoda MojeMetoda virtuální, tak jste se explicitním odkazem na implementaci metody v bázové třídě o virtuální chování mnohdy nechtěně a navíc hloupě připravili. Pěkný je i článek 45, který dostačujícím a přehledným způsobem popíše význam šablonových funkcí (nejen) na příkladu užitečného zobecněného kopírovacího konstruktoru se šablonou a nechybí  v něm ani vysvětlení, proč v C++ zobecněný a na šablonách založený konstruktor nikdy nenahradí běžný nešablonový konstruktor.  Důležitá je i kapitola, která shrnuje, proč byste měli nečlenské funkce definovat přímo v šablonové třídě, jestliže požadujete, aby všechny argumenty metody podporovaly automatické konverze. Opomenut není ani význam rysů (traits) u šablonového programování.

V další kapitole Scott zmíní, kdy je vhodné nahradit standardní operátory new a delete a také, jak nejlépe vyhovět všem konvencím při nahrazování operátorů new a delete. (článek 52 pojednává o nutnosti poskytnout "placement" operator delete, jestliže jsme se rozhodli používat vlastní placement "new" operator)

V poslední kapitole s radami, které se nevešly jinam, kniha dává na pranýř ty programátory, kteří si myslí, že varování kompilátoru nejsou přece žádnou chybou a že tedy na ně nemusejí brát ohledy, protože by jim zkazila nadšení, že tu svou nádhernou sbírku chybových bajtů zkompilovali, nebo protože si už vypěstovali vůči varovným hláškám selektivní slepotu. Poslední dva články vyzývají vývojáře, aby si osvojili algoritmy, kontejnery, iterátory a postupy využité v STL a v studnici nápadů zvané BOOST.

Líbí  se mi, že na konci každého článku je rychlé shrnutí, co byste si měli určitě zapamatovat.

Ani kniha Effective C++ není dokonalá a najdeme v ní mnoho kapitol, u nichž si zkušenější vývojář řekne, že existují i pokročilejší postupy. Například v článcích o objektech a šablonách by se slušelo zmínit, jak pomocí částečné specializace můžeme odstraňovat/přidávat modifikátory const.

template<class T>struct ConstHelper

typedef const T Add;

typedef T Remove;  

typedef const T Invert;  

 

 

};

template<class T>struct ConstHelper<const T>

{  

typedef const T Add;

typedef T Remove;  

typedef T Invert;  

 

};

 

typedef typename ConstObject<T>::Add const_parameter;

 

Šablony a jejich využití při metaprogramování je vměstnáno do jednoho přehledového článku ("článek 48 - Be Aware of template metaprogramming). Výjimky jsou zmíněny jen okrajově. Tyto výtky ale platí jen zčásti, protože Scott napsal druhý díl nazvaný More Effective C++ a dluh vůči knihovně STL splatil v další knize Effective STL.

Scottovi se podařilo napsat čtivou a přitom po odborné stránce nijak neošizenou knihu, jejíž brilantní formulace i postřehy jsou projevem zajímavé osobnosti, která netrpí tou příšernou a nudnou falešnou skromností, která alespoň mě vadí u jiných autorů, okázale zdůrazňujících ve svém díle  všechny spolupracovníky, známé, přicmrdávače i vtěrky, aby za každou cenu dali najevo, jak oceňují práci druhých a jak jsou jen malým mravenečkem při stavbě velkého díla, jímž je jejich kniha. Tento druh povinné a společensky přívětivě přijímané falešné skromnosti mi totiž přijde jen nepříliš rafinovanou zástěrkou pro skutečnou a nelichotivou skromnost duševní (snad jen té :-) ) potence a navýsost egoistickou snahou možný publikační neúspěch (pod) průměrného, frázovitého, a dokonce mnohdy i po té, tolikrát zdůrazňované jako jediné významné či rozhodující, odborné stránce nepřesného díla omluvit a zdůvodnit slovy: "Podívejte se, že za ten paskvil opravdu nemohu sám". Bohužel ani kvantitativně početné stádo nevyvolených duševně chudých nemůže supeřit s jedním Scottem Meyersem. Scott si nebere žádné servítky a jak se dá z jeho knihy vytušit, nečiní mu žádný problém v jím (spolu)pořádaných anketách nominovat a jistě po těžké úvaze :-) i zvolit na čelní místa (a já dodávám zcela oprávněně) sebe a své knihy. A svým kritikům, kteří na něho vytahují, že není žádný programátor s rozsáhlou praxí, s klidem výstižně odpovídá, že on strávil mnoho času studiem jazyka a všech jeho rysů včetně slepých uliček právě proto, aby ostatním pomohl v jejich praktickém programování.  Teoretický i praktický rozum se doplňují a nekonkurují si. Je tristní pohled na vývojáře, který si osvojil 10% syntaktických i sémantických konstrukcí a knihoven z  nepřeberného bohatství každého programovacího jazyka a pak své praktické "informační systémy" plácá s pomocí jedné bábovičky. Osobně tomu říkám syndrom "všechno vyřeší volání grid.DataBind()", což ale je srozumitelné asi jen ASP.NET vývojářům, ale podobné typy vývojářů(?) se najdou všude.

Motem knihy je citát z Petroniova Satyriconu: "Wisdom and Beauty form a very rare combination".  Této knize se to podařilo.



Wednesday, 06 August 2008 18:29:08 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  Ostatní


 Wednesday, 30 July 2008
GSM Net Monitor verze 0.7.0. Alfa

Homepage aplikace.

Instalační cab.

Návod na rozchození lokalizace pozice pomocí BTS

Změny ve verzi  0.0.7.

  1. Odstraněna chyba, která se mohla vyskytnout při odesílání požadavku na server => Net Monitor vypisoval chybu 2 (špatné formátování).

 

Důležité:

Před instalací nové verze vypněte v aplikaci sledování sítě.

Protože se jedná o AlFA preview, doporučuji před instalaci Net Monitoru mít v zařízení např. SPB Pocket Plus a v něm aktivovaný safe boot - jestliže by vám "vytuhlo" zařízení, nebudete muset dělat HR (Hard Reset), protože můžete při startu zařízení dočasně deaktivovat Today pluginy.

Po upgradu budete muset pravděpodobně znovu zadat svůj registrovaný email a přístupový kód. Pokud jste jej zapomněli, jděte na stránku http://gsmadmin.renestein.net a zadejte znovu svůj email. Aplikace vám nabídne opětovné zaslání emailu.



Wednesday, 30 July 2008 16:06:40 (Central Europe Standard Time, UTC+01:00)       
Comments [4]  Mobilitky | Nativní kód | Net Monitor