\


 Monday, 23 August 2010
C# - kontrola existence vlastnosti u typu dynamic bez vyvolání výjimky RuntimeBinderException.

Dan Steigerwald mě na Facebooku upozornil na článek “Challenge: Dynamically dynamic” na blogu Ayende Rahiena. Jak se můžete sami podívat, celá výzva se týká toho, jak zjistit, jestli u dané instance typu dynamic existuje vlastnost se zadaným jménem, aniž byste museli odchytávat  výjimku RuntimeBinderException, která vás na chybějící vlastnost sice drsně upozorní, ale zároveň vás nutí používat kód řízený výjimkami.

 

Jak vypadá kód detekující existenci vlastnosti s vy/zneužitím RuntimeBinderException?

   private static bool HasPropertyNaive(IDynamicMetaObjectProvider dynamicProvider, string name)
        {
            try
            {
                var callSite =
                                CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program),
                                                         new[]
                                                                 {
                                                                     CSharpArgumentInfo.Create(
                                                                         CSharpArgumentInfoFlags.None, null)
                                                                 }));
                callSite.Target(callSite, dynamicProvider);
                return true;
            }
            catch (RuntimeBinderException)
            {

                return false;
            }

        }

A použití:

static void Main(string[] args)
        {
            dynamic testDynamicObject = new ExpandoObject();
            testDynamicObject.Name = "Testovaci vlastnost";
            Console.WriteLine(HasPropertyNaive(testDynamicObject, "Name"));
            Console.WriteLine(HasPropertyNaive(testDynamicObject, "Id"));            
            Console.ReadLine();
        }

 

Stejně jako v zadání na blogu Ayende metoda HasPropertyNaive pracuje s každým objektem dynamic skrytým za rozhraním IDynamicMetaObjectProvider. V metodě napodobíme chování kompilátoru C# – vytvoříme “kontext operace”, tzv. CallSite, které předáme hlavně tzv. “Binder” voláním tovární metody metody Binder.GetMember. Binder, v našem případě binder pro get akcesor vlastnosti, jejíž přítomnost testujeme a jejíž název jsme předali metodě HasPropertyNaive v argumentu name,  si lze zjednodušeně představit jako objekt, který je odpovědný za dohledání hodnoty vlastnosti u dynamického objektu za běhu aplikace.

U CallSite použijeme metodu Target, které předáme samotnou instanci callSite a objekt dynamic, u nějž chceme otestovat existenci vlastnosti. Jestliže vlastnost u objektu dynamic neexistuje, metoda Target vyvolá výjimku RuntimeBinderException a my vrátíme false, jinak ignorujeme návratovou hodnotu metody target a vracíme true, což je pro kód volající metodu HasPropertyNaive potvrzení, že vlastnost existuje.

Metoda HasPropertyNaive plní svůj účel, ale za cenu vyvolání výjimky RuntimeBinderException. A toho se týká právě “challenge”. Zkusme se výjimky zbavit.

Kdybychom měli testovat existenci vlastnosti jen u instancí “ExpandoObject”, měli bychom hned hotovo.

private static bool HasPropertyExpandOnly(IDynamicMetaObjectProvider dynamicProvider, string name)
{
return ((IDictionary)dynamicProvider).ContainsKey(name);
}

ExpandoObject totiž podporuje rozhraní IDictionary a klíčem v objektu Dictionary jsou názvy vlastností.

Zadání ale vyžaduje, abychom zkontrolovali přítomnost vlastnosti u ktreréhokoli objektu dynamic typu IDynamicMetaObjectProvider. Když předáte metodě HasPropertyExpandOnly instanci dynamic, která dědí z DynamicObject nebo přímo implementuje rozhraní IDynamicMetaObjectProvider, při pokusu o přetypování instance na rozhraní IDictionary dojde k výjimce.

Problém s detekcí přítomnosti vlastnosti by také zcela zmizel, kdybychom měli zaručeno, že každá instance typu IDynamicMetaObjectProvider a s ní asociovaný “DynamicMetaObject” z metody GetDynamicMemberNames vrátí seznam s názvy všech dynamických členů.

private static bool HasProperty(IDynamicMetaObjectProvider dynamicProvider, string name)
{
return dynamicProvider
                    .GetMetaObject(Expression.Constant(dynamicProvider))
                    .GetDynamicMemberNames()
                    .Contains(name);
} 

Bohužel ani to garantováno nemáme a metoda GetDynamicMemberNames u mnoha instancí dynamic bez skrupulí vrátí prázdné pole, i když vlastnosti existují.

Musíme si tedy poradit jinak.

Následuje kód metody HasProperty včetně podpůrných konstrukcí, která pracuje s libovolnou instanci typu IDynamicMetaObjectProvider a ke zjištění, zda je, či není vlastnost přítomna, nepotřebuje vyvolávat výjimku RuntimeBinderException.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;


namespace DynamicCheckPropertyExistence
{
    class Program
    {                
        private static bool HasProperty(IDynamicMetaObjectProvider dynamicProvider, string name)
        {



            var defaultBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program),
                             new[]
                                     {
                                         CSharpArgumentInfo.Create(
                                         CSharpArgumentInfoFlags.None, null)
                                     }) as GetMemberBinder;


            var callSite = CallSite<Func<CallSite, object, object>>.Create(new NoThrowGetBinderMember(name, false, defaultBinder));


            var result = callSite.Target(callSite, dynamicProvider);

            if (Object.ReferenceEquals(result, NoThrowExpressionVisitor.DUMMY_RESULT))
            {
                return false;
            }

            return true;

        }

      

    }

    class NoThrowGetBinderMember : GetMemberBinder
    {
        private GetMemberBinder m_innerBinder;        
        
        public NoThrowGetBinderMember(string name, bool ignoreCase, GetMemberBinder innerBinder) : base(name, ignoreCase)
        {
            m_innerBinder = innerBinder;            
        }
        
        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
        {


            var retMetaObject = m_innerBinder.Bind(target, new DynamicMetaObject[] {});            
            
            var noThrowVisitor = new NoThrowExpressionVisitor();
            var resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);

            var finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
            return finalMetaObject;

        }
        
    }

    class NoThrowExpressionVisitor : ExpressionVisitor
    {        
        public static readonly object DUMMY_RESULT = new DummyBindingResult();
        
        public NoThrowExpressionVisitor()
        {
            
        }

        protected override Expression VisitConditional(ConditionalExpression node)
        {
            
            if (node.IfFalse.NodeType != ExpressionType.Throw)
            {
                return base.VisitConditional(node);
            }
            
            Expression<Func<Object>> dummyFalseResult = () => DUMMY_RESULT;
            var invokeDummyFalseResult = Expression.Invoke(dummyFalseResult, null);                                    
            return Expression.Condition(node.Test, node.IfTrue, invokeDummyFalseResult);
        }

        private class DummyBindingResult {}       
    }
}

Proč se metoda HasProperty obejde bez vyvolání výjimky? Použil jsem trik, kdy objektu CallSite nepředávám přímo výchozí GetMemberBinder, ale vlastní NoThrowGetMemberBinder, který je potomkem bázové třídy GetMemberBinder z DLR. Můj NoThrowGetMember v kostruktoru přijímá další objekt GetMemberBinder, který interně použije pro zjištění hodnoty vlastnosti. Metoda HasProperty předává instanci NoThrowGetMember do konstruktoru tovární metodou Binder.CreateBinder vytvořený výchozí C# Binder, takže nemusíme v třídě NoThrowGetMember naštěstí duplikovat veškerou logiku pro přístup k vlastnosti, která je  již součástí výchozího C# Binderu.

NoThrowGetBinderMember se spoléhá na to, že při pokusu o přístup k dynamickým metodám a vlastnostem u objektu IDynamicMetaProvider třída GetMemberBinder dovoluje odvozeným třídám, aby aplikovaly vlastní logiku pro práci s “dynamickými” členy v tzv. “fallback” metodách. NoThrowGetBinderMember tedy dostane šanci dohledat vlastnost v přepsané metodě FallbackGetMember.

Metoda FallbackGetMember pracuje takto:

1. Použije metodu Bind předaného Binderu (m_innerBinder) , které předá jako první argument “DynamicMetaObject” v argumentu target a druhým argumentem je prázdné pole objektů “DynamicMetaObject”. Výchozí Binder udělá svou práci a vrátí nám další DynamicMetaObject, který si uložíme do proměnné retMetaObject.

var retMetaObject = m_innerBinder.Bind(target, new DynamicMetaObject[] {});
 

2. V retMetaObject je vyhodnocovací výraz (Expression tree) pro získání hodnoty vlastnosti, který pro vlastnost Name může vypadat zjednodušeně takto. Tučně je vyznačena část, která je odpovědná za vyvolání výjimky, jestliže vlastnost neexistuje.

IIF(ExpandoTryGetValue(Convert($arg0), value(System.Dynamic.ExpandoClass), 0, "Name", False, value), value, throw(new RuntimeBinderException("'System.Dynamic.ExpandoObject' does not contain a definition for 'Name'")))
IIF(ExpandoCheckVersion(Convert($arg0), value(System.Dynamic.ExpandoClass)), {var value; ... }, gotoCallSiteBinder.UpdateLabel)

My ale výjimku vyvolávat nechceme, a proto vlastním vizitorem NoThrowExpressionVisitor modifikujeme “expression tree” tak, že  místo vyvolání výjimky, když vlastnost neexistuje, vrátíme hodnotu statické proměnné  DUMMY_RESULT. Vlastnost Expression u proměnné retMetaObject je určena pouze pro čtení, proto vytvoříme nový DynamicMetaObject s upravenou “Expression” a původními restrikcemi a uložíme ho do proměnné finalMetaObject, která je také návratovou hodnotou metody FallbackGetMember.

 


            
            var noThrowVisitor = new NoThrowExpressionVisitor();
            var resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);

            var finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
            return finalMetaObject;

Úplný kód třídy NoThrowExpressionVisitor naleznete ve výpisu výše.

Metoda HasProperty  vyvolá metodu Target na objektu CallSite a zkontroluje, zda její návratová hodnota je referenčně shodná s hodnotou v proměnné NoThrowExpressionVisitor.DUMMY_RESULT a pokud tomu tak je, vrátí false, protože nyní místo vyvolání výjimky byla vrácena zástupná hodnota signalizující “vlastnost u objektu dynamic neexistuje”, jinak vrátí true -  “vlastnost existuje”.

 

var result = callSite.Target(callSite, dynamicProvider);
if (Object.ReferenceEquals(result, NoThrowExpressionVisitor.DUMMY_RESULT))
            {
                return false;
            }
            
            

Použití metody HasProperty.

static void Main(string[] args)
        {
            dynamic testDynamicObject = new ExpandoObject();
            testDynamicObject.Name = "Testovaci vlastnost";            
            Console.WriteLine(HasProperty(testDynamicObject, "Name"));
            Console.WriteLine(HasProperty(testDynamicObject, "Id"));
            Console.ReadLine();
        }

/*Výsledek:
True 
False
*/

Zkoušel jsem metodu HasProperty použít i na zjišťování existence vlastnosti u potomků třídy DynamicObject pro zpracování rss a vše funguje dle očekávání.

“Challenge” pokořen. :-) Obvyklá poznámka na závěr – za nic neručím, kód nemusí fungovat v dalších verzích DLR, C# a .Net Frameworku, ale to vy určitě víte.:)



Monday, 23 August 2010 14:33:03 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | C# | LINQ | Programátorské hádanky


 Monday, 11 September 2006
Programátorská hádanka - 11.9.2006

Máte jednoduchý (a dnes bych řekl dokonce až urážlivě triviální) kód. ;-) A cituji jednoduchou otázku Mistra kódování a alžbětinského Sira W. Shakespeara - Jak se vám líbí?

class MessageQueue
 {
   private static object messageLock = new object();
   private List<Message> list;
    
    public MessageQueue()
    {
      list = new List<Message>();
    }

     public void RemoveMessage(Message message)
     {
         lock (messageLock)
         {
             list.Remove(message);
         }
     }
  }



Monday, 11 September 2006 13:31:35 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  Programátorské hádanky


 Sunday, 02 July 2006
Řešení programátorské hádanky z 24.6.2006 - ThreadStartDelegate

Jak bylo patrné ze zadání "hádanky", snažil jsem se, abyste mi napsali, jaké postupy používáte pro předání argumentů a získání návratové hodnoty z metody, když jste omezeni signaturou delegátu ThreadStartDelegate. Třída Thread dokáže pracovat pouze s metodami bez návratových hodnot (void) a také:

a) Metoda nesmí mít žádné argumenty  - delegát ThreadStartDelegate

b) Metody přijímající jeden argument typu object - delegát ParameterizedThreadStart (pouze v  .Net Frameworku 2.0).

Jak jste se zmínili v komentářích - když chceme předat argumenty kódu vykonávanému v samostatném threadu, můžeme použít třídy s vlastním stavem a návratovou hodnotu získat třeba tak, že si zaregistrujeme "callback" funkci.

Mně by se ale líbilo, kdybych měl  jen jednu "stavovou" třídu s argumenty pro metody s jakoukoli signaturou a tedy společnou pro všechny rozdílné typy delegátů a hlavně chci objekt z této třídy přímo předat do konstruktoru třídy Thread, aby se můj kód nijak nelišíl od přímého použití delegátů ThreadStart a ParameterizedThreadStart.  Proto jsem v komentářích mluvil o eleganci zvoleného řešení :)

    class ThreadStartDelegateWrapper
    {
        #region Private members
        private Delegate m_innerDelegate;
        private object[] m_methodParameters;
        private object m_returnValue;
        #endregion Private members

        #region Construtors
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="del">Delegát ukazující na metodu, která poběží v jiném threadu</param>
        public ThreadStartDelegateWrapper(Delegate del)
        {
            if (del == null)
            {
                throw new ArgumentNullException("del");
            }
            m_innerDelegate= del;
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="del">Delegát ukazující na metodu, která poběží v jiném threadu</param>
        ///<param name="methodParameters">Argumenty metody</param>
        public ThreadStartDelegateWrapper(Delegate del, params object[] methodParameters) : this(del)
        {
            m_methodParameters = methodParameters;
        }

        #endregion Construtors

        #region Public properties
        /// <summary>
        /// Argumenty metody
        /// </summary>
        public object[] MethodParameters
        {
            get
            {
                return m_methodParameters;
            }
            set
            {
                m_methodParameters = value;
            }
        }

        /// <summary>
        /// Návratová hodnota metody
        /// </summary>
        public object ReturnValue
        {
            get
            {
                return m_returnValue;
            }
        }
        #endregion Public properties

        #region Operators
        /// <summary>
        /// Implicitní konverzní operátor, který instanci třídy <see cref="ThreadStartDelegateWrapper"/> převede na delegáta <see cref="ThreadStartDelegate"/>
        /// </summary>
        /// <param name="originalWrapper">Instance třídy <see cref="ThreadStartDelegateWrapper"/></param>
        /// <returns>Instanci delegáta <see cref="ThreadStartDelegate"/>, který ukazuje na metodu zapouzdřující zavolání jakéhokoli delegáta</returns>
        public static implicit operator ThreadStart(ThreadStartDelegateWrapper originalWrapper)
        {
            if (originalWrapper == null)
            {
                throw new ArgumentNullException("originalWrapper");
            }

            return new ThreadStart(originalWrapper.InvokeDelegate);
        }
        #endregion Operators

        #region private methods
        //Metoda, jejíž signatura odpovídá signatuře deklarované delegátem ThreadStartDelegate
        private void InvokeDelegate()
        {
            m_returnValue = m_innerDelegate.DynamicInvoke(m_methodParameters);
        }
        #endregion private methods
    }
 

Třídě ThreadStartDelegateWrapper můžete do konstruktoru předat odkaz na jakéhokoli delegáta a také můžete předat všechny potřebné argumenty metodě, na kterou delegát ukazuje (argument methodParameters v konstruktoru). Předané argumenty můžete kdykoli upravit, protože jsou zveřejněny ve vlastnosti MethodParameters.

Abych mohl svoji třídu použít kdekoli je očekáván delegát ThreadStart, napsal jsem implicitní konverzní operátor třídy ThreadStartDelegateWrapper na delegát ThreadStartDelegate.

Návratovou hodnotu metody vykonané v jiném threadu nalezneme ve vlastnosti ReturnValue - nic nám také nebrání přidat událost, ve které získanou návratovou hodnotu budeme sami aktivně distribuovat všem zájemcům.

A tady je jednoduchý ukázkový kód.

    class Test
    {
        public delegate string TestDelegate(string message);

        public string WriteMessage(string message)
        {
            Console.WriteLine(message);
            return "OK";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test myTestClass = new Test();
            Test.TestDelegate myDelegate = myTestClass.WriteMessage;

            ThreadStartDelegateWrapper wrapper = new ThreadStartDelegateWrapper(myDelegate, new object[] { "Hello world" });
            Thread myThread = new Thread(wrapper);
            myThread.Start();
            myThread.Join();

            Console.WriteLine(wrapper.ReturnValue);
            Console.Read();
            
        }
    }

Je jednoduché rozšířit třídu ThreadStartDelegateWrapper o podporu dalších delegátů v .NET Frameworku (např. WaitCallback), takže na metody s různými signaturami mohou ukazovat typičtí delegáti v .Net Frameworku  s využitím jedné jediné třídy fungující jako obecný adaptér mezi naším kódem a kódem v .Net Frameworku.



Sunday, 02 July 2006 14:40:08 (Central Europe Standard Time, UTC+01:00)       
Comments [3]  .NET Framework | Programátorské hádanky


 Saturday, 24 June 2006
Programátorská hádanka - jak metodě v delegátu ThreadStart předat argumenty?

Jak asi víte, delegát ThreadStart v .Net Frameworku může ukazovat pouze na metodu, která nemá žádné argumenty a vrací void.

Jak nejlépe metodě v delegátu ThreadStart předat argumenty? A navíc  - dokázali byste, aby metoda spuštěná v delegátu ThreadStart vrátila hodnotu, nebo si teď už opravdu jen vymýšlím po dobrém sobotním bourbonu nějaké rozkošné .Net 3.0 pohádky? ;-) Napadlo mě totiž docela elegantní řešení, tak by mě zajímalo, jestli ho někdo z vás už zná a používá? :-)

 



Saturday, 24 June 2006 22:28:13 (Central Europe Standard Time, UTC+01:00)       
Comments [12]  .NET Framework | Programátorské hádanky


 Thursday, 27 April 2006
Řešení hádanky z 18-.4. - vztahy Include, Extend v případech užití

Protože kolegové v diskuzi pod původním spotem podrobně rozebrali všechny aspekty vztahu Include a Extend v případech užití,  je tento můj spot z větší části sumarizující. V čem tedy byl "chyták"? Cílem hádanky bylo upozornit na to, že vztahy Include a Extend nemusejí být tak jednoduše rozpoznatelné, jak se dočtete v různých rychlopříručkách,  a také jsem chtěl, abyste si všimli, že vztah Extend bylo bylo příjemné zpřesnit tím, co sám pro sebe nazývám "anonymní datové sloty".

Tak toto byl začátek dvoustránkového spotu, který se mi podařilo omylem smazat. :-( Protože už se mi nechce teď psát celý spot znovu, tak jen v krátkosti zrekapituluji, čeho se hádanka týkala.

Specifikace UML popisuje vztahy Include a Extend takto:

"Include is a directed relationship between two use cases, implying that the behavior of the included use case is inserted into the behavior of the including use case. The including use case may only depend on the result (value) of the included use case. This value is obtained as a result of the execution of the included use case."

"This relationship specifies that the behavior of a use case may be extended by the behavior of another (usually supplementary) use case. The extension takes place at one or more specific extension points defined in the extended use case. Note, however, that the extended use case is defined independently of the extending use case and is meaningful independently of the extending use case. On the other hand, the extending use case typically defines behavior that may not necessarily be meaningful by itself. Instead, the extending use case defines a set of modular behavior increments that augment an execution of the extended use case under specific conditions. Note that the same extending use case can extend more than one use case. Furthermore, an extending use case may itself be extended."

 

V hádance nemohl být vztah mezi případem užití 'Založení objednávky' a 'Platba kartou' Include ani Extend. Include nelze použít, protože máme i jiné typy plateb, a případ užití Platbu kartou tedy nelze vložit povinně (dalším typem platby bude třeba dobírka - žádné záludnosti ohledně plateb jsem přichystány neměl, jen jsem chtěl, abyste se jako správní analytici pídili po podrobném zadání :-) ). Nejedná se ale ani o vztah Extend, protože rozšířený případ užití nijak nezávisí na rozšiřujícím případu užití a nemůže číst žádné jeho "návratové hodnoty"  - pro nás to znamená, že nezjistíme, jestli platba prošla.

Řešením je vytvoření nového abstraktního případu užití nazvaného třeba 'Kontrola platby', který přes vazbu Include provážeme s případem užití 'Založení objednávky' . Konkrétními potomky budou "Kontrola platby kartou", "Kontrola ostatních typů plateb". Vazba Include nám dovoluje číst návratovou hodnotu vloženého případu užití. V alternativním toku událostí případu užití  'Založení objednávky' podle vybraného typu platby vložíme konkrétní případ užití a dle výsledku vkládaného případu užití dokončíme hlavní tok událostí. Když neprojde platba kartou, můžeme zákazníkovi nabídnout výběr jiného typu platby.

Hlavně ale platí - celá hádanka i její řešení je jen hříčka bez užitku pro zákazníka, která demostruje, jak lze i méně časté vztahy řešit běžnými výrazovými prostředky jazyka UML. Stále platí, že vazby Include, Extend ani gen-spec zákazníkovi raději moc neukazujte, jestliže  nechcete zbytečně trávit čas vysvětlováním vztahů, které jsou pro projekt málo podstatné a pro zákazníka samotného většinou jen iritující.



Thursday, 27 April 2006 18:25:16 (Central Europe Standard Time, UTC+01:00)       
Comments [13]  Programátorské hádanky | UML


 Tuesday, 18 April 2006
Zajímavý dotaz (hádanka) na případy užití v UML - vztah Include, Extend, anebo ...?

Po základní analýze jste zjistili, jak zákazníci vašeho klienta vytvářejí objednávky:

"Zákazník v našem obchodě vybírá zboží a poté si může vybrat, jak jej uhradí. Jedním z možných typů platby je platba kartou. K založení objednávky dojde pouze tehdy, pokud zadané údaje o platební kartě jsou platné, zákazník má dostatečnou hotovost na účtu a platba tedy projde."

Řekněme, že z těchto údajů vytvoříme (kromě dalších, zde nezmiňovaných) dva případy užití:

  1. UC001 - Založení objednávky zákazníkem
  2. UC002 - Platba kartou

Jaký vztah je podle UML mezi těmito případy užití ? Odpověď není tak jednoduchá, jak se může na první (a dokonce i druhý a ležérní třetí) pohled zdát... :-)



Tuesday, 18 April 2006 09:27:39 (Central Europe Standard Time, UTC+01:00)       
Comments [27]  Programátorské hádanky | UML


 Sunday, 26 February 2006
Analyticko-programátorská hádanka (vztahy mezi třídami)
Hádanka

Máte následující jednoduchý diagram tříd. Je na něm něco "podivného"? Abyste to neměli tak jednoduché, tak řeknu, že diagram je součástí přednášek Ing. Kamila Svobody, který na univerzitě v Hradci Králové vyučuje objektové modelování. Takže se vás třeba jen snažím zmást a ukázat, jak diagram precizně zpracovává své téma ;-) Anebo opravdu najdete nějaké nelogičnosti, na které by měl být kolega Svoboda upozorněn? ;-)

diagram tříd

Sunday, 26 February 2006 17:21:19 (Central Europe Standard Time, UTC+01:00)       
Comments [38]  Programátorské hádanky


 Saturday, 30 July 2005
Řešení hádanky "Znáte dobře návrhové vzory"

Protože nikdo nedodal kompletní řešení k hádance z 26.7., zde je odpověď.

  1. Třída MessageReceiverBase - událost MessageReceived, návrhový vzor Observer.
  2. Třída MessageReceiverBase -metoda CreateMessage, návrhový vzor Factory Method. Metoda CreateMessage vrací přímo instancí třídy Message nebo její potomky. Potomci třídy MessageReceiverBase mohou metodu přepsat a vrátit z metody například instanci OrderMessage, aniž by byly dotčeny vysokoúrovňové scénaře pracující pouze s rozhraním Message a deklarované na úrovni MessageReceiverBase.
  3. Třídy MessageReceivePoint, MessageReceiverBase a její potomky můžeme považovat za participanty návrhového vzoru Bridge. Třída MessageReceivePoint je abstrakcí - logickým přístupovým bodem, který dokáže přijímat data na daném URI a který interně využívá konkrétní fyzický přístupový bod, jímž je potomek třídy MessageReceiverBase zapouzdřující detaily komunikace po zvoleném přenosovém protokolu. Metoda Listen třídy MessageReceivePoint deleguje volání na metodu Listen třídy MessageReceiverBase. Samozřejmě lze o potomcích třídy MessageReceiveBase uvaživat i jako o strategiích, jak zaznělo v diskuzi o spotu, i když si myslím, že vzor Bridge lépe vyjadřuje role tříd.
  4. Třída AddMesageAttributesProcessor - jak vyjadřuje její název, jedná se o realizátora vzoru Content Enricher - k přijaté zprávě dodává další informace, které nebyly přímo její součástí ale které jsou důležité pro další zpracování zprávy. Příklad - messagingový systém přijme objednávku a Content Enricher ke zprávě doplní údaje o platební morálce zákazníka.
  5. Třída FilterMessageAttributesProcessor - opět dle názvu můžete usuzovat, že jde o návrhový vzor Content Filter. Procesor z přijaté zprávy odstraní všechny informace, které nejsou důležité pro další zpracování a které by pouze zbytečně vytěžovaly zdroje serveru. Příklad - messagingový systém přijme obrázové přílohy, které není třeba posílat k dalšímu zpracování, ale pouze se archivují v DMS, takže nemá smysl hnát obrázky celým procesem vyřizování objednávky. Content Filter obrázky ze zprávy před jejím dalším zpracováním odstraní.

Jak někteří z vás (Petr :) ) správně vytušili, diagram také svádí k tomu, aby byl rozšířen o Intercepting filter nebo o vzor Pipes&Filters - tyto vzory v něm ale v současné podobě nalezneme poze jako latentní možnosti, které můžeme uskutečnit doplněním a úpravou vztahů mezi existujícími třídami.



Saturday, 30 July 2005 17:22:25 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Návrhové vzory | Programátorské hádanky


 Tuesday, 26 July 2005
Hádanka - znáte dobře návrhové vzory?

Dokážete na následujícím diagramu najít všechny použité návrhové vzory? ;-)

Pokud by to někomu přišlo trapně jednoduché, můžete dávat návrhy na vylepšení diagramu...

BTW: Ten diagram není z reálného projektu, takže můžete opravdu usuzovat na návrhové vzory jen podle vztahů, případně podle názvů elementů v diagramu. Je to jen hra ;-)

Stažení diagramu



Tuesday, 26 July 2005 17:01:52 (Central Europe Standard Time, UTC+01:00)       
Comments [14]  Programátorské hádanky


 Wednesday, 30 March 2005
Programátorsko-analytická hádanka
Hádanka

Máte třídu objednávky, která může nabývat 3 stavů (Nová, Schválená, Zamítnutá) . Dokážete určit, co všechno je v následujícím kódu špatně?

 
 
 public enum OrderState
    {
        New,
        Rejected,
        Approved
    }
    
    
    public class Order
    {
        protected OrderState m_state;
        public Order()
        {
            m_state = OrderState.New;
        }
        public virtual void Approve()
        {
            if (m_state != OrderState.New)
            {
                throw new InvalidOperationException();
            }
            m_state = OrderState.Approved;
        }
        
        public virtual void Reject()
        {
            if (m_state != OrderState.New)
            {
                throw new InvalidOperationException();
            }
            m_state = OrderState.Rejected;
        }
    }
    
    public class SpecialOrder : Order
    {
        public SpecialOrder() : base()
        {
            
        }
        
        public override void Approve()
        {
            
            throw new InvalidOperationException();
        }
        public override void Reject()
        {
            if ((m_state != OrderState.New) && (m_state != OrderState.Approved))
            {
                throw new InvalidOperationException();
            }
            
            m_state = OrderState.Rejected;
        }
        
    }
    


Wednesday, 30 March 2005 19:31:00 (Central Europe Standard Time, UTC+01:00)       
Comments [24]  Programátorské hádanky


 Tuesday, 09 November 2004
Další listopadové úterý s programátorskou hádankou

Hádanka Dokážete (opět bez spuštění programu ;) ) určit, co vypíše tento program, a zdůvodnit jeho chování?

 
 
 
using System;
namespace RSTein.Blog
{
    struct NumberStruct
    {
        public int Number;
    }
    
    
    public class Test 
    {
        private  static Object Add(Object obj) 
        {
        
            if (obj is NumberStruct)
            {
                NumberStruct numb = (NumberStruct) obj;
                numb.Number += 1;
                return numb;
                
            }
            
            return obj;
        
        }
        public static void Main()
        {
            NumberStruct numberStruct = new NumberStruct();
            numberStruct.Number = 10;
            
            Object retNumberStructObject = Add(numberStruct);
            object originalStructObject =  numberStruct;
            
            Console.WriteLine(numberStruct.Number);
            Console.WriteLine(((NumberStruct)retNumberStructObject).Number);
            Console.WriteLine((originalStructObject == retNumberStructObject));
            Console.Read();
        }
    }

}


Tuesday, 09 November 2004 09:16:00 (Central Europe Standard Time, UTC+01:00)       
Comments [7]  Programátorské hádanky


 Tuesday, 26 October 2004
Další úterý s programátorskou hádankou
Máte tento kód:
using System;
namespace Blog.Test
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    class DelegateTest
    {
        public delegate int CallOperation (int x, int y);
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            CallOperation operation = new CallOperation(Add);
            operation += new CallOperation(Substract);
            int result = operation( 2, 2);
            Console.WriteLine(result);
            Console.Read();
        }

        public static int Add(int x, int y)
        {
            return (x + y);
        }
        public static int Substract(int x, int y)
        {
            return (x - y);
        }
    }
}

Otázka 1) Dokážete bez spuštění programu říct, jaký výsledek bude v proměnné result a proč?

Otázka 2) Umíte přepsat tento kód tak, abyste dostali návratové hodnoty z obou metod, na něž ukazuje kompozitní delegát?



Tuesday, 26 October 2004 18:50:00 (Central Europe Standard Time, UTC+01:00)       
Comments [8]  Programátorské hádanky


 Tuesday, 19 October 2004
Programátorská hádanka na nudné úterý

Dokážete najít dva ekvivalentní zápisy zámku (lock) v metodě AtomicOperation?

    class  AtomicTest
    {
        private int x = 0;
        private int y = 0;
        public void AtomicOperation(int arg)
        {
            lock(this)
            {
                x += arg;
                y += arg;
            }
        }
    }


Tuesday, 19 October 2004 17:27:00 (Central Europe Standard Time, UTC+01:00)       
Comments [16]  Programátorské hádanky


 Thursday, 07 October 2004
Dokážete najít všechny chyby v kódu?

Dokážete poznat všechny nesmysly v tomto kódu? Nejde o žádný reálný příklad, jen o letmé zachycení a nakupení střípků potměšilých nočních programátorských můr, jimiž je moje podvědomí neodbytně zaplavováno a mučeno po duševně drásající  refaktorizaci cizího kódu...;)

using System;
using System.Collections;
namespace DirtyProject.BusinessModel
{
    public class Order
    {
        private int m_id;
        private ArrayList m_orderItems;
        public Order(int id)
        {
            m_id = id;
            LoadOrSave(false);
            m_orderItems = new ArrayList();
        }
        public virtual void DeleteAndNotifyCustomer()
        {
            //Implementace
        }
        public virtual void LoadOrSave(bool save)
        {
            //implementace perzistence
        }
        //Položky objednávky
        public ArrayList OrderItems
        {
            get
            {
                return m_orderItems;
            }
        }
    }

    public class OrderItem
    {
        private Order m_order;
        private int m_orderItemsCount;
        private decimal m_orderItemsPrice;
       
        public OrderItem(Order parent)
        {
            if (parent == null)
                throw new ArgumentNullException();
           
            m_order = parent;
            m_orderItemsCount = 0;
            m_orderItemsPrice = 0;
        }
        public Order Parent
        {
            get
            {
                return m_order;               
            }
            set
            {
                m_order = value;   
            }
        }
        //Počet kusů zboží v objednávce
        public virtual int OrderItemsCount
        {
            get
            {
                return m_orderItemsCount;
            }
            set
            {
                m_orderItemsCount = value;;
            }
        }
        //Celková cena všech kusů zboží v položce objednávce
        public virtual decimal OrderItemsPrice
        {
            get
            {
                return m_orderItemsPrice;
            }
            set
            {
                m_orderItemsPrice = value;
            }
        }
        public virtual void LoadOrSave(bool save)
        {
            //implementace perzistence
        }
    }
}

 



Thursday, 07 October 2004 20:31:00 (Central Europe Standard Time, UTC+01:00)       
Comments [18]  Programátorské hádanky


 Thursday, 23 September 2004
Druhá malá programátorská hádanka na tento týden

Dokážete (bez spuštění programu) poznat, v čem bude provádění tohoto kódu z hlediska "selského programátorského rozumu" podivné a proč?

using

System;

namespace

ConsoleApplication24

{

public class Class1

{

public static void Calculate( int number)

{

}

}

public class Class2: Class1

{

public static void Calculate( long number)

{

}

}

class Test

{

[STAThread]

static void Main( string []args)

{

Class2.Calculate(

int .MinValue);

}

}

}



Thursday, 23 September 2004 15:30:00 (Central Europe Standard Time, UTC+01:00)       
Comments [13]  Programátorské hádanky


 Tuesday, 21 September 2004
Malá programátorská hádanka

Máte následující kód:

class

Test :  BaseTest

{

private string m_rene;

public Test() : base ()

{

m_rene = "Rene";

}

public override void WriteName()

{

Console.WriteLine(m_rene);

Console.Read();

}

}

Metoda WriteName vypíše za určitých podmínek jen prázdný řetězec - víte kdy? ;)

Update: A víte, jak se bude chovat stejný kód po přepsání do C++?



Tuesday, 21 September 2004 15:32:00 (Central Europe Standard Time, UTC+01:00)       
Comments [25]  Programátorské hádanky