Click here to Skip to main content
15,888,026 members
Articles / Programming Languages / C#

Dynamic Method Dispatcher

Rate me:
Please Sign up or sign in to vote.
4.98/5 (74 votes)
16 Apr 2012CPOL23 min read 135.5K   1.4K   114  
No more long switch statements!
/*  
    DynamicMethodDispatcher and MuticastDynamicMethodDispatcher classes:
    Generic class DynamicMethodDispatcher dispatches a call to a generic delegate isntance found by a dictionary key
    Generic class MuticastDynamicMethodDispatcher is a multi-case version of DynamicMethodDispatcher.
    
    Copyright (C) 2006-2011 by Sergey A Kryukov
    http://www.SAKryukov.org
*/
namespace SA.Universal.Technology {
    using System.Collections.Generic;

    /// <summary>
    /// Exception type used in DynamicMethodDispatcher and MuticastDynamicMethodDispatcher
    /// when a KEY key is not found in the dictionary of delegates
    /// </summary>
    /// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
    /// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
    public class DynamicMethodNotFoundException<KEY, MESSAGE> : System.Exception {
        internal DynamicMethodNotFoundException(KEY key, MESSAGE message) { this.FKey = key; this.FMessage = message; }
        public KEY KeyValue { get { return this.FKey; } }
        public MESSAGE MessageValue { get { return this.FMessage; } }
        KEY FKey; MESSAGE FMessage;
    } //class DynamicMethodNotFoundException

    /// <summary>
    /// Base class for DynamicMethodDispatcher and MuticastDynamicMethodDispatcher
    /// </summary>
    /// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
    /// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
    /// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
    public abstract class DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
        /// <summary>
        /// Delegate type used for dynamic method dispartching
        /// </summary>
        /// <param name="key">Key of the delegate dictionary</param>
        /// <param name="message">Message of the message passed to Invoke methods of Dispatcher</param>
        /// <returns>RETURN</returns>
        public delegate RETURN DynamicMethod(KEY key, MESSAGE message);
        /// <summary>
        /// Indexes returning length of invocation list for a key
        /// </summary>
        /// <param name="key">Key key used to check up the length of invocation list</param>
        /// <returns>Returns 0 is key is not found, otherwise the length of invocation list</returns>
        public int this[KEY key] {
            get {
                DynamicMethod method;
                if (Dictionary.TryGetValue(key, out method))
                    return method.GetInvocationList().Length;
                return 0;
            } //get this
        } //this
        /// <summary>
        /// Property returns all keys in Dynamic Method dictionary
        /// </summary>
        /// <returns>Array of KEY; all keys in Dynamic Method dictionary</returns>
        KEY[] Keys {
            get {
                KEY[] result = new KEY[Dictionary.Keys.Count];
                Dictionary.Keys.CopyTo(result, 0);
                return result;
            } //get Keys
        } //Keys
        #region implementation
        internal protected RETURN SingleCastInvoke(KEY key, MESSAGE message) {
            DynamicMethod method;
            if (Dictionary.TryGetValue(key, out method))
                return method.Invoke(key, message);
            throw new DynamicMethodNotFoundException<KEY, MESSAGE>(key, message);
        } //SingleCastInvoke
        internal protected bool SingleCastTryInvoke(KEY key, MESSAGE message, out RETURN returnValue) {
            DynamicMethod method;
            bool success = Dictionary.TryGetValue(key, out method);
            if (success)
                returnValue = method.Invoke(key, message);
            else
                returnValue = default(RETURN);
            return success;
        } //SingleCastTryInvoke
        internal protected Dictionary<KEY, DynamicMethod> Dictionary = new Dictionary<KEY, DynamicMethod>();
        #endregion implementation
    } //DynamicMethodDispatcherBase

    /// <summary>
    /// Dispatcher type which allows only no more than one dynamic method per key
    /// </summary>
    /// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
    /// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
    /// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
    public class DynamicMethodDispatcher<KEY, MESSAGE, RETURN> : DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
        /// <summary>
        /// Adds a dynamic method for a key if the key is not found in the invocation dictionary
        /// </summary>
        /// <param name="key">Key for the invocation dictionary</param>
        /// <param name="method">Dynamic method</param>
        /// <returns>Returns true a method is successfully added; false means the key is already found</returns>
        public bool Add(KEY key, DynamicMethod method) {
            if (Dictionary.ContainsKey(key)) return false;
            Dictionary.Add(key, method);
            return true;
        } //AddReplace
        /// <summary>
        /// This invocation method throws exception if key is not found
        /// </summary>
        public RETURN Invoke(KEY key, MESSAGE message) { return SingleCastInvoke(key, message); }
        /// <summary>
        /// This invocation method does not throw exception if key is not found, returns false instead
        /// </summary>
        public bool TryInvoke(KEY key, MESSAGE message, out RETURN returnValue) { return SingleCastTryInvoke(key, message, out returnValue); }
    } //class DynamicMethodDispatcher
    
    /// <summary>
    /// Multicast variant of DynamicMethodDispatcher
    /// </summary>
    /// <typeparam name="KEY">Key type of the delegate dictionary</typeparam>
    /// <typeparam name="MESSAGE">Message type of the message passed to Invoke methods of Dispatcher</typeparam>
    /// <typeparam name="RETURN">Return type expected in return of the Invoke methods of Dispatcher</typeparam>
    public class MuticastDynamicMethodDispatcher<KEY, MESSAGE, RETURN> : DynamicMethodDispatcherBase<KEY, MESSAGE, RETURN> {
        /// <summary>
        /// Accumulation method used to return Invocation result accumulated from all methods in the invocation list
        /// </summary>
        /// <param name="index">Index of the call in invocation list</param>
        /// <param name="key">Dictionary key of the delegate instance being invoked</param>
        /// <param name="message">Message passed during delegate invocation</param>
        /// <param name="lastResultAvailable">True is the last result returned from the previous call to the accumulation method is available</param>
        /// <param name="lastResult">Last result returned from the previous call to the accumulation method</param>
        /// <param name="currentResult">Currect result returned from the method of the invocation list</param>
        /// <returns>Returns accumulated result after the call to a member of the invocation list</returns>
        public delegate RETURN AccumulationMethod(
            int index,
            KEY key, MESSAGE message,
            bool lastResultAvailable, RETURN lastResult, RETURN currentResult);
        /// <summary>
        /// Adds a dinamic method by a dictionary key to the available invocation list or creates new delegate instance
        /// and adds it to invocation dictionary
        /// </summary>
        public void Add(KEY key, DynamicMethod method) {
            DynamicMethod delegateInstance;
            if (Dictionary.TryGetValue(key, out delegateInstance)) {
                Dictionary.Remove(key); //adding method to invokation list will change delegate's identity (wow!):
                delegateInstance += method;
            } else
                delegateInstance = method; 
            Dictionary.Add(key, delegateInstance);
        } //AddReplace
        /// <summary>
        /// This invocation method does not cause exception if a key is not found but returns null
        /// </summary>
        /// <returns>Returns null if a key is not found, otherwise an array of all return values
        /// returned from every member of invocation list</returns>
        public RETURN[] Invoke(KEY key, MESSAGE message) {
            DynamicMethod delegateInstance;
            if (Dictionary.TryGetValue(key, out delegateInstance)) {
                System.Delegate[] invocationList = delegateInstance.GetInvocationList();
                int index = 0;
                RETURN[] returnValue = new RETURN[invocationList.Length];
                foreach (System.Delegate method in invocationList) {
                    returnValue[index] = ((DynamicMethod)method)(key, message);
                    index++;
                } //loop invocationList
                return returnValue;
            } else
                return null;
        } //Invoke
        /// <summary>
        /// This invocation method uses <seealso cref="AccumulationMethod"></seealso>
        /// to accumulate all values returned by all members of invocation list in one value;
        /// this method can throw exception is key is not found />
        /// </summary>
        /// <returns>Accumulated result of invocation, depending on invocation list and accumulation method</returns>
        public RETURN Invoke(KEY key, MESSAGE message, AccumulationMethod accumulation) {
            bool dummyFound;
            return AccumulatedInvoke(key, message, accumulation, true, out dummyFound);
        } //Invoke
        /// <summary>
        /// This method is a "try" variant of invocation method using Accumulation method,
        /// it does not throw exception but return success status
        /// </summary>
        /// <param name="returnValue">Accumulated result of invocation, depending on invocation list and accumulation method</param>
        /// <returns>True is invocation was successful, false if key is not found</returns>
        public bool TryInvoke(KEY key, MESSAGE message, AccumulationMethod accumulation, out RETURN returnValue) {
            bool found;
            returnValue = AccumulatedInvoke(key, message, accumulation, false, out found);
            return found;
        } //Invoke
        /// <summary>
        /// This invocation method ignore results returned by the members of invocation list during invocaion;
        /// it can throw exception if key is not found
        /// </summary>
        void InvokeNoReturn(KEY key, MESSAGE message) {
            SingleCastInvoke(key, message);
        } //InvokeNoReturn
        /// <summary>
        /// This is a "try" variant of <seealso cref="InvokeNoReturn"/>; it does not throw exception if key is not found
        /// </summary>
        /// <returns>True is invocation was successful, false if key is not found</returns>
        public bool TryInvokeNoReturn(KEY key, MESSAGE message) {
            RETURN dummyReturnValue;
            return SingleCastTryInvoke(key, message, out dummyReturnValue);
        } //TryInvokeNoReturn
        #region implementation
        public RETURN AccumulatedInvoke(KEY key, MESSAGE message, AccumulationMethod accumulation, bool throwException, out bool found) {
            if (accumulation == null) { //fallback:
                if (throwException) {
                    found = true;
                    InvokeNoReturn(key, message);
                } else
                    found = TryInvokeNoReturn(key, message);
                return default(RETURN);
            } //if no accumulation method
            DynamicMethod delegateInstance;
            found = Dictionary.TryGetValue(key, out delegateInstance);
            if (found) {
                System.Delegate[] invocationList = delegateInstance.GetInvocationList();
                int index = 0;
                RETURN lastResult = default(RETURN);
                foreach (System.Delegate method in invocationList) {
                    bool lastResultAvailable = index > 0;
                    RETURN currentResult = ((DynamicMethod)method)(key, message);
                    lastResult = accumulation(index, key, message, lastResultAvailable, lastResult, currentResult);
                    index++;
                } //loop invocationList
                return lastResult;
            } else
                if (throwException)
                    throw new DynamicMethodNotFoundException<KEY, MESSAGE>(key, message);
                else
                    return default(RETURN);
        } //AccumulatedInvoke
        #endregion implementation
    } //class MuticastDynamicMethodDispatcher

} //namespace SA.Universal.Technology

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Physics, physical and quantum optics, mathematics, computer science, control systems for manufacturing, diagnostics, testing, and research, theory of music, musical instruments… Contact me: https://www.SAKryukov.org

Comments and Discussions