Delegate calling methods with differing parameters

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Elliot
    New Member
    • Sep 2010
    • 7

    #1

    Delegate calling methods with differing parameters

    Is there a way to to declare a delegate, the instance of which will call methods with differing parameter numbers and types?

    Here are excerpts of the code I am currently using:

    Code:
    public delegate void CalcArrayDelegate();
    ...
    public static CalcArrayDelegate arrayMethod;
    ...
    public void AssignElement(List<VariableType> _masterVariableList, CalcArrayDelegate _calcMethod)
            {
                for (int i = 0; i < COUNTVARS; i++)
                {
                    switch (this[i].GetType().ToString())
                    {
                        case "Type1":
                            {
                                Type1 currVar = this[i] as Type1;
                                string formula = String.Empty;
                                double value = 0;
                                
                                // DELEGATE WITH THREE PARAMETERS, WITH VALUE (DOUBLE)
                                _calcMethod(i, ref value, ref formula);
                                ...
                                break;
                            }
    
                        case "Type2":
                            {
                                Type2 currVar = this[i] as Type2;
                                double value = 0;
                                // DELEGATE WITH TWO PARAMETERS, WITH VALUE (DOUBLE)
                                 _calcMethod(i, ref value);
                                inVar.AssignValue(value);
                                break;
                            }
    
                        case "Type3":
                            {
                                Type3 currVar = this[i] as Type3;
                                bool value = false;
                                // DELEGATE WITH TWO PARAMETERS, WITH VALUE (BOOLEAN)
                                 _calcMethod(i, ref value);
                                inVar.AssignValue(value);
                                break;
                            }
    ...
    Many thanks for any help on this.
  • Airslash
    New Member
    • Nov 2007
    • 221

    #2
    I'm not really sure if it's going to help you, but I think it can point you to the correct course for getting it working:

    I have an Interface designed like this:

    Code:
        public interface IParser<T>  where T : System.EventArgs
        {
            // Functions
            void StartParsing(int interval);
            void StopParsing();
    
            // Properties
            bool Parsing { get; }
            bool Asynchronous { get; set; }
            Core.MemoryBuffer Buffer { get; set; }
    
            // Events
            event EventHandler<T> OnMessage;
        }
    Here I use a generic type to make a public event with type T as delegate/eventhandler. All my classes implementing this interface have their own set of parameters for passing this into the function.
    Below is the code example for my parser implementation:

    Code:
        public class VTMessageParser : IParser<EventArgs.VTMessageEventArgs>
        {
            #region Constructor
    
            /// <summary>
            /// Creates a new instance of the IParser, specifalized for handeling IVTMessages.
            /// </summary>
            /// <param name="buffer">The MemoryBuffer that will hold all the data.</param>
            public VTMessageParser(Core.MemoryBuffer buffer)
            {
                Buffer = buffer;
            }
    
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// Initializes the internal components to start processing the data that
            /// is present in the MemoryBuffer. Places the IParser in processing mode.
            /// </summary>
            /// <param name="interval"></param>
            public void StartParsing(int interval)
            {
                // Call the uninitialize routine first to ensure that we
                // we have no rogue timers around.
                StopParsing();
    
                // Initialize the timer for the parsing routine.
                InitializeProcessTimer(interval);
    
                // Change our internal flag to true.
                Parsing = true;
            }
    
            /// <summary>
            /// Uninitializes the internal components and stops the IParser from 
            /// processing data in the MemoryBuffer. Places the IParser in idle mode.
            /// </summary>
            public void StopParsing()
            {
                // Uninitialize the timer for the parsing routine.
                UninitializeProcessTimer();
    
                // Change our flag to false.
                Parsing = false;
            }
    
            #endregion
    
            #region Public Properties
    
            /// <summary>
            /// Gets a value indicating whether the IParser is currently parsing
            /// data. Read-only property.
            /// </summary>
            public bool Parsing
            {
                get { return m_parsing; }
                protected set { m_parsing = value; }
            }
    
            /// <summary>
            /// Gets or sets the internal buffer of the ClientParser
            /// </summary>
            public Core.MemoryBuffer Buffer
            {
                get { return m_buffer; }
                set { m_buffer = value; }
            }
    
            /// <summary>
            /// Gets or sets a value indicating whether events are raised synchronously or asynchronously from the code.
            /// Synchronous events provide a linear but slower processing time, whereas asynchronous events provider a
            /// parallell but faster approach to parsing.
            /// </summary>
            public bool Asynchronous
            {
                get { return m_asynchronous; }
                set { m_asynchronous = value; }
            }
    
            #endregion
    
            #region Public Events
    
            /// <summary>
            /// This event is raised whenever the parser finds a complete IVTMessage 
            /// in the internal buffer and has extracted that message.
            /// </summary>
            public event EventHandler<EventArgs.VTMessageEventArgs> OnMessage
            {
                add { m_event_message += value; }
                remove { m_event_message -= value; }
            }
    
            #endregion
    
            #region Private Methods
    
            /// <summary>
            /// Initializes the internal timer to run the processing routine at the specified
            /// interval.
            /// </summary>
            /// <param name="interval">The interval in ms to run the process routine.</param>
            void InitializeProcessTimer(int interval)
            {
                m_timer_process = new Timer(interval);
                m_timer_process.AutoReset = true;
                m_timer_process.Elapsed += new ElapsedEventHandler(ProcessRoutine);
                m_timer_process.Enabled = true;
            }
    
            /// <summary>
            /// Uninitializes the internal timer and halts the processing.
            /// Releases all resources claimed by the timer as well.
            /// </summary>
            void UninitializeProcessTimer()
            {
                if (m_timer_process != null)
                {
                    m_timer_process.Enabled = false;
                    m_timer_process.Elapsed -= ProcessRoutine;
                    m_timer_process.Dispose();
                    m_timer_process = null;
                }
            }
    
            /// <summary>
            /// Processes the data currently in the buffer of the IParser implementation.
            /// If the buffer contains a complete IVTMessage implementation, the function
            /// will extract the IVTMessage and raise the onMessage event.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void ProcessRoutine(object sender, ElapsedEventArgs e)
            { 
                // Try to obtain the lock for the processing routine first.
                // If the lock cannot be obtained, then simply fall through the function.
                // With this lock, we only allow one concurrent processing routine.
                if (System.Threading.Monitor.TryEnter(m_lock_process))
                {
                    // Because we have claimed a lock on something, we also need to release
                    // this lock. If we don't release this lock, deadlocking will occur.
                    try
                    {
                        // Keep track of the current position in the buffer.
                        int start_position = 0;
    
                        // Loop through the memory buffer.
                        while (start_position < Buffer.Length - 4)
                        {
                            // Extract 4 bytes from the memory buffer and encode them to an ASCII string.
                            string section = Encoding.ASCII.GetString(Buffer.Read(start_position, 4));
    
                            // Check if that strings forms the start of a IVTMessage.
                            if (string.Equals(section, "@VT:"))
                            {
                                // Keep track of the offset position for our message.
                                int offset = start_position + 4;
    
                                // Loop through the remaining bytes in the buffer.
                                while (offset < Buffer.Length)
                                {
                                    // Extract the byte pointed at and encode it into a string.
                                    section = Encoding.ASCII.GetString(Buffer.Read(offset, 1));
    
                                    // Check if that string forms the end of a IVTMessage.
                                    if (string.Equals(section, "#"))
                                    {
                                        // Extract the complete IVTMessage.
                                        Messages.IVTMessage message = IVTMessageBuilder.Construct(Buffer.Read(start_position, offset + 1));
                                        
                                        // Raise the event.
                                        RaiseOnMessageEvent(message);
    
                                        // Remove the IVTMessage from the buffer.
                                        Buffer.Delete(start_position, offset + 1);
    
                                        // Reset the index position and break the inner for loop.
                                        start_position = 0;
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                // IVTMessage header was not found at the specified location, so increase the index
                                // position by one.
                                start_position++;
                            }
                        }
                    }
                    finally
                    {
                        // Always release the lock we have claimed.
                        System.Threading.Monitor.Exit(m_lock_process);
                    }
                }
            }
    
            /// <summary>
            /// Raises the OnMessage event of the IParser implementation asynchronous or 
            /// synchronous from the calling code depending on the settings of the class.
            /// </summary>
            /// <param name="message">The IVTMessage that has been found by the IParsers.</param>
            void RaiseOnMessageEvent(Messages.IVTMessage message)
            { 
                // Copy the event handler to a local variable to prevent racing conditions between
                // invoking the event and unsubscribing from the event.
                EventHandler<EventArgs.VTMessageEventArgs> handle = m_event_message;
    
                // Check if there are subscribers for this event. If the handle is null
                // then no functions are subscribed to the event.
                if (handle != null)
                {
                    // Get the invocation list for the event handler so all subscribers
                    // are notified.
                    Delegate[] invocationList = handle.GetInvocationList();
    
                    // Loop through the list and invoke each delegate.
                    foreach (Delegate del in invocationList)
                    {
                        // Cast the delegate to the correct type, prior to invoking it.
                        EventHandler<EventArgs.VTMessageEventArgs> ehandle = del as EventHandler<EventArgs.VTMessageEventArgs>;
    
                        try
                        {
                            if (Asynchronous) ehandle.BeginInvoke(this, new EventArgs.VTMessageEventArgs(message), new AsyncCallback(CleanOnMessageDelegate), null);
                            else ehandle(this, new EventArgs.VTMessageEventArgs(message));
                        }
                        catch {/*empty catch block.*/}
                    }
                }
            }
    
            /// <summary>
            /// Releases all resources claimed by the delegate that was responsible for
            /// invoking the OnMessage event asynchronous from the calling code.
            /// </summary>
            /// <param name="result">The IAsyncResult of the asynchronous invoke.</param>
            void CleanOnMessageDelegate(IAsyncResult result)
            {
                // Cast the IAsyncResult result to a concrete AsyncResult implementation.
                System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
    
                // Access the delegate, cast it to the correct type and release all the resources.
                (aresult.AsyncDelegate as EventHandler<EventArgs.VTMessageEventArgs>).EndInvoke(result);
            }
    
            #endregion
    
            #region Private Fields
    
            Core.MemoryBuffer m_buffer;                                 // The MemoryBuffer holding the data.
            EventHandler<EventArgs.VTMessageEventArgs> m_event_message; // The event handler for raising the onMessage event.
            Timer m_timer_process;                                      // The timer to raise the process routine.
    
            readonly object m_lock_process = new object();              // Lock for mutual exclusion of threads in the process routine.
            bool m_asynchronous = false;                                // Value indicating whether to work synchronous or asynchronous.
            bool m_parsing = false;                                     // Value indicating if the IParser is parsing.
    
            #endregion
        }
    I'm pretty sure you can do the same with your delegate, but declaring it with a specific base type and have your implementations use a derived type as argument array.

    Comment

    • Elliot
      New Member
      • Sep 2010
      • 7

      #3
      Thanks--I'll take a look at your proposed solution.

      Comment

      • Aimee Bailey
        Recognized Expert New Member
        • Apr 2010
        • 197

        #4
        Take a close look at the Console.WriteLi ne() function, it uses a special keywork called params to turn an ordinary array of objects into an expandable list of arguments! From what I understand, there is no problem with including this in a delegate sequence. so heres a test example:

        Code:
        void WriteToConsole(string format, params object[] args)
        {
          Console.WriteLine("There were {0} params passed to this method!",
                                     args.Length);
        }
        Hope this helps you refactor and hone your code.

        Aimee.

        Comment

        • Christian Binder
          Recognized Expert New Member
          • Jan 2008
          • 218

          #5
          Create a class, give it your three delegate forms as delegate and use method-overloading.
          I think, it's not possible to do a delegate-overloading (same delegate name for different parameter-lists)
          Using object[] params won't work, because it doesn't support by reference parameters.

          Look at my example and if you have question, feel free to ask me :-)

          Code:
          public class TestClass {
              public void Start() {
                CalcMethods methods = new CalcMethods();
                //set the delegates 
                //methods.Method1 = ...
                //methods.Method2 = ...
                //methods.Method3 = ...
          
                List<VariableType> list = null;
                AssignElement(list, methods);
              }
          
              public void AssignElement(List<VariableType> _masterVariableList, CalcMethods _calcMethods) {
                int i = 0;
                //your switch statement ...
                string type = "";
                switch (type) {
                  case "Type1": {
                      string formula = string.Empty;
                      double value = 0D;
                      // DELEGATE WITH THREE PARAMETERS, WITH VALUE (DOUBLE)
                      _calcMethods.CallCalcMethod(i, ref value, ref formula);
                      break;
                    }
                  case "Type2": {
                      double value = 0;
                      // DELEGATE WITH TWO PARAMETERS, WITH VALUE (DOUBLE)
                      _calcMethods.CallCalcMethod(i, ref value);
                      break;
                    }
                  case "Type3": {
                      bool value = false;
                      // DELEGATE WITH TWO PARAMETERS, WITH VALUE (BOOLEAN)
                      _calcMethods.CallCalcMethod(i, ref value);
                      break;
                    }
                }
              }
            }
          
            public delegate void CalcWithThreeParametersWithValueDouble(int i, ref double value, ref string formula);
            public delegate void CalcWithTwoParametersWithValueDouble(int i, ref double value);
            public delegate void CalcWithTwoParametersWithValueBool(int i, ref bool value);
          
            public class CalcMethods {
              public CalcWithThreeParametersWithValueDouble Method1;
              public CalcWithTwoParametersWithValueDouble Method2;
              public CalcWithTwoParametersWithValueBool Method3;
          
              public void CallCalcMethod(int i, ref double value, ref string formula) {
                Method1(i, ref value, ref formula);
              }
          
              public void CallCalcMethod(int i, ref double value) {
                Method2(i, ref value);
              }
          
              public void CallCalcMethod(int i, ref bool value) {
                Method3(i, ref value);
              }
          
          
            }

          Comment

          Working...