overloading and polymorphism pains

Last post 02-22-2007 6:47 PM by zwitterion. 8 replies.

Sort Posts:

  • overloading and polymorphism pains

    02-20-2007, 5:39 PM
    • Loading...
    • zwitterion
    • Joined on 08-25-2006, 7:08 PM
    • Posts 29

    Hi all, I have a situation which I feel should be workable, but it seems like I've indirected myself into a corner. Anyone familiar with C# might be able to tell me straight up what's possible or not:

    I have an "event" class hierarchy:

    public interface IEvent;
    
    public class EventA : IEvent;
    
    public class EventB : IEvent;

     and a handler hierarchy:

    abstract class EventHandler
    {
      List queue;
    
      GoForIt()
      {
       foreach(IEvent e in queue)
         ProcessEvent(e);
      }
    
      abstract void ProcessEvent(IEvent e);
    }
    
    class SpecificEventHandler : EventHandler
    {
      virtual void ProcessEvent(IEvent e)
      { /*catch all processing?*/ }
    
       public void ProcessEvent(EventA e)
       { /*specific EventA processing*/ }
    
       public void ProcessEvent(EventB e)
       { /*specific EventA processing*/ }
    }

     Now ideally I'd like to add events of various types to the handler's queue and have it figure out at runtime which overload to call based on the objects actual type. This does not do it, obviously - what happens is the compiler (i assume) decides it's dealing with IEvents, always, and calls ProcessEvent(IEvent) for all events in that loop there.

    I get the feeling that I might be asking the impossible, but is there some way I can unbox the event dynamically and have it invoke the processevent method for its most derived type? I'd much rather use inheritance and polymorphism to do this than throw a whole bunch of explicit overloads in there or worse, giant conditional chains or switch statements...

  • Re: overloading and polymorphism pains

    02-20-2007, 5:54 PM
    • Loading...
    • ask_Scotty
    • Joined on 01-06-2007, 10:52 AM
    • Warwick
    • Posts 707

    Hello my friend,

    Have you ever heard of covariance and contravariance?  They are new in C# 2.0 and I think they will help you here.  Read up on them as they are very useful for this sort of thing. 

    Kind regards

    Scotty

     

  • Re: overloading and polymorphism pains

    02-21-2007, 5:53 AM

    In your scenario Contravariance will be helpful. Please find the sample code snippet. Not sure whether the code will exactly fit your need but it would give you an idea. The example uses polymorphism with delegates (hope this is what you are looking for) whereas the example in the below link uses method overloading.

    Please refer to the following link for more information
    http://msdn2.microsoft.com/en-us/library/ms173174.aspx
    http://codebetter.com/blogs/raymond.lewallen/archive/2006/12/28/Covariance-and-Contravariance.aspx

        public class EventA : IEvent
        {
            public void Execute()
            {
                MessageBox.Show("Inside EventA");
            }
        }

        public class EventB : IEvent
        {
            public void Execute()
            {
                MessageBox.Show("Inside EventB");
            }
        }

        public class EventC : IEvent
        {
            public void Execute()
            {
                MessageBox.Show("Inside EventC");
            }
        }

        public class EventHandler
        {
             public delegate void EventHdlr(IEvent e);
             List<IEvent> queue = new List<IEvent>();

            public void AddEvent(IEvent e)
            {
                queue.Add(e);
            }

        public void GoForIt()
        {
             EventHdlr evt;
             foreach(IEvent e in queue)
             {
                  evt = EventMethod;
                  evt(e);
             }
        }

        public static void EventMethod(IEvent e)
        {
              e.Execute();
        }
    }

     hope this helps.

    Sundar

    Sundar
    ________________________________________________________
    Please don't forget to mark all the replies that helped you as "Answer"
  • Re: overloading and polymorphism pains

    02-21-2007, 5:58 AM

    I forgot to add the code snippet for using the above code. 

    EventHandler eventHdlr = new EventHandler();
    eventHdlr.AddEvent(new EventA());
    eventHdlr.AddEvent(new EventB());
    eventHdlr.AddEvent(new EventC());
    eventHdlr.GoForIt();

    Sundar

    Sundar
    ________________________________________________________
    Please don't forget to mark all the replies that helped you as "Answer"
  • Re: overloading and polymorphism pains

    02-21-2007, 9:58 PM
    Answer
    • Loading...
    • mbanavige
    • Joined on 11-06-2003, 1:29 PM
    • New England, USA
    • Posts 7,755
    • Moderator
      TrustedFriends-MVPs

    You may also wish to explore using a Visitor Design Pattern.

    http://en.wikipedia.org/wiki/Visitor_pattern

    http://www.dofactory.com/Patterns/PatternVisitor.aspx

    It seems to be a real good fit for what you are describing.  Here's a quick example.

    Partial Class _Default
        Inherits System.Web.UI.Page
    
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
            'this class gets passed to each event object we vill visit
            Dim visitor As New EventHandler
    
            'set up a list containing different Types of objects
            Dim list As New ArrayList
            list.Add(New EventA)
            list.Add(New EventB)
            list.Add(New EventC)
            list.Add(New EventC)
            list.Add(New EventB)
            list.Add(New EventA)
    
            For Each obj As IEvent In list
                'notifiy the object that we are visiting it.
                'it will "call us back" which is where we learn it's concrete type
                obj.Visit(visitor)
            Next
    
            'this just demonstrates the flexibility of the visitor pattern
            'notice how the IEvent objects do not care 
            'which visitor is visiting them.
            Dim anotherVisitor As New EventHandlerEx
            For Each obj As IEvent In list
                obj.Visit(anotherVisitor)
            Next
    
        End Sub
    
    End Class
    
    Public Interface IEvent
        'all visitable objects (the concrete Events) will implement this interface
        Sub Visit(ByVal visitor As IEventVisitor)
    End Interface
    
    'these are the classes that represent our events
    'to keep the event processing centralized/organized, 
    'it actually occurs in the visitor and not in each event object
    Public Class EventA
        Implements IEvent
        Public Sub Visit(ByVal visitor As IEventVisitor) Implements IEvent.Visit
            'EventA "calls back" to the visitor
            visitor.Accept(Me) 'call our visitors accept method that takes Type: EventA
        End Sub
    End Class
    Public Class EventB
        Implements IEvent
        Public Sub Visit(ByVal visitor As IEventVisitor) Implements IEvent.Visit
            'EventB "calls back" to the visitor
            visitor.Accept(Me) 'call our visitors accept method that takes Type: EventB
        End Sub
    End Class
    Public Class EventC
        Implements IEvent
        Public Sub Visit(ByVal visitor As IEventVisitor) Implements IEvent.Visit
            'EventC "calls back" to the visitor
            visitor.Accept(Me) 'call our visitors accept method that takes Type: IEvent
        End Sub
    End Class
    
    
    Public Interface IEventVisitor
        Sub Accept(ByVal evnt As IEvent)
        Sub Accept(ByVal evnt As EventA)
        Sub Accept(ByVal evnt As EventB)
    End Interface
    
    'We can create as many classes as we want to represent our visitors
    Public Class EventHandler
        Implements IEventVisitor
        Public Sub Accept(ByVal evnt As IEvent) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Processing IEvent<br />")
        End Sub
        Public Sub Accept(ByVal evnt As EventA) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Processing EventA<br />")
        End Sub
        Public Sub Accept(ByVal evnt As EventB) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Processing EventB<br />")
        End Sub
    End Class
    
    'this just demonstrates the flexibility of the visitor pattern
    Public Class EventHandlerEx
        Implements IEventVisitor
        Public Sub Accept(ByVal evnt As IEvent) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Another way to process IEvent<br />")
        End Sub
        Public Sub Accept(ByVal evnt As EventA) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Another way to process EventA<br />")
        End Sub
        Public Sub Accept(ByVal evnt As EventB) Implements IEventVisitor.Accept
            'this is where the event can be processed based on its concrete type
            HttpContext.Current.Response.Write("Another way to process EventB<br />")
        End Sub
    End Class
     
    Mike Banavige
    ~~~~~~~~~~~~
    Dont forget to click "Mark as Answer" on the post that helped you.
    This credits that member, earns you a point and marks your thread as Resolved so we will all know you have been helped.
  • Re: overloading and polymorphism pains

    02-22-2007, 12:18 PM
    • Loading...
    • zwitterion
    • Joined on 08-25-2006, 7:08 PM
    • Posts 29

    @Sundar: Thanks, that's a very neat implementation, it would work great except that I wanted another level of indirection in the handlers so that any given event could be handled a number of different ways.

    @Mike - Perfect! That's a great solution, exactly what I was looking for. Only used the visitor pattern maybe once before so it didn't come to mind, thanks for the suggestion and nice example.

    What a weird forum this is - people post replies, and the replies are both really intelligent and useful.. Smile

  • Re: overloading and polymorphism pains

    02-22-2007, 4:29 PM
    • Loading...
    • zwitterion
    • Joined on 08-25-2006, 7:08 PM
    • Posts 29
     ... except that it doesn't work. My code is summarized below:
    public abstract class EventLogger
    {
    public Queue<Event> eventQueue;
    public void ProcessEvents()
    {
    while(eventQueue.Count > 0)
    VisitEvent(eventQueue.Dequeue());
    }
    public virtual void VisitEvent(Event e) { e.Visit(this); }
    public virtual void ProcessEvent(Event e) {}
    }

    public class AppEventLogger : EventLogger
    {
    //I would like it if I didn't have to do this but
    //I put it in in a last ditch effort to get it to
    //invoke the right method
    public override void VisitEvent(Event e) { e.Visit(this); }

    public override void ProcessEvent(Event e) {/*catchall processing*/}
    public override void ProcessEvent(EventA e) {/*special processing*/}
    public override void ProcessEvent(EventB e) {/*special processing*/}
    }

    public abstract class Event
    {
    //some properties... public virtual void Visit(EventLogger visitingLogger)
    { visitingLogger.ProcessEvent(this); }

    }

    public class EventA : Event
    {
    //again, wish i didn't have to put this on all subclasses public override void Visit(EventLogger visitingLogger)
    { visitingLogger.ProcessEvent(this); }
    }

    public class EventB : EventA
    {
    //again, wish i didn't have to put this on all subclasses public override void Visit(EventLogger visitingLogger)
    { visitingLogger.ProcessEvent(this); }
    }

     What actually happens is when I try to process an EventB with an AppEventLogger is:

    1. EventLogger.ProcessEvents() dequeues an EventB and calls VisitEvent on it
    2. AppEventLogger.VisitEvent(e) is invoked with the EventB as the argument. It calls Visit on the event.
    3. EventB.Visit is invoked with the AppEventLogger as its argument. It calls ProcessEvent on the eventlogger argument, passing itself (and EventB) back.
    4. The method actually invoked next is AppEventLogger.ProcessEvent(Event e), or EventLogger.ProcessEvent(Event e) if it is not overriden in the subclass. NOT AppEventLogger.ProcessEvent(EventB e) as I would expect.
    The example above does not use the same class hierarchy, using interfaces instead. I imagine this is the difference that makes it work there and not here. All the same, I see no reason that this SHOULDN'T work. Does anyone understand why?
  • Re: overloading and polymorphism pains

    02-22-2007, 4:45 PM
    • Loading...
    • mbanavige
    • Joined on 11-06-2003, 1:29 PM
    • New England, USA
    • Posts 7,755
    • Moderator
      TrustedFriends-MVPs

    without the interface, you would need to define all of the ProcessEvent signatures in your abstract class or in a base class

    public abstract class EventLogger{ 
         public Queue<Event> eventQueue; 
         public void ProcessEvents() 
         {  
               while(eventQueue.Count > 0)   
                  VisitEvent(eventQueue.Dequeue()); 
         } 
         public virtual void VisitEvent(Event e) { e.Visit(this); } 
         public virtual void ProcessEvent(Event e) {}
         public virtual void ProcessEvent(EventA e) {}
         public virtual void ProcessEvent(EventB e) {}
    }

     

    Mike Banavige
    ~~~~~~~~~~~~
    Dont forget to click "Mark as Answer" on the post that helped you.
    This credits that member, earns you a point and marks your thread as Resolved so we will all know you have been helped.
  • Re: overloading and polymorphism pains

    02-22-2007, 6:47 PM
    • Loading...
    • zwitterion
    • Joined on 08-25-2006, 7:08 PM
    • Posts 29

    Yes I just figured that out by going through your example.

    I'm tossing up whether to do exactly that, or creating optionally implementable interfaces such as

    public interface EventALogger
    {
       public void ProcessEvent(EventA);
    }
    public interface EventBLogger
    {
       public void ProcessEvent(EventB);
    }
    
    //and apply them optionally to the logger (handler) classes:
    
    public AppEventLogger : EventLogger, EventALogger
    {
      public void ProcessEvent(Event e) {}
      public void ProcessEvent(EventA e) {}
    }
    This makes the logger classes more encapsulated I suppose, just have to decide whether that actually buys me anything to offset the verbosity.
Page 1 of 1 (9 items)
Microsoft Communities
Page view counter