3

I have an adapter pattern on a generic class that essentially adapts between types:

class A<T> { event EventHandler e; }
class Aadapter<T1, T2> : A<T1> { A<T2> a; Aadapter(A<T2> _a) { a = _a; }  }

The problem is that A contains an event. I effectively want all event handlers assigned to Adapter to fall through to a.

It would be awesome if I could assign the a's event handler to adapter's event handler but this is impossible?

The idea here is that A is almost really just A but we need a way to adapt the them. Because of the way event's work I can't how to efficiently do it except manually add two event handlers and when they are called they "relay" the to the other event. This isn't pretty though and it would seem much nicer if I could have something like

class A<T> { event EventHandler e; }
class Aadapter<T1, T2> : A<T1> { event *e; A<T2> a; Aadapter(A<T2> _a) { a = _a; e = a.e; }  }

in a sense we have a pointer to the event that we can assign a2's event to.

I doubt there is any simple way but maybe someone has some idea to make it work.

(BTW, I realize this is possible with virtual events but I'd like to avoid this if at all possible)

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
Stretto
  • 486
  • 2
  • 10

3 Answers3

2

I think that this is what you are after:

    class A<T>
    {
        public virtual event EventHandler e;
    }

    class Aadapter<T1, T2> : A<T1> 
    { 
        A<T2> a; 
        Aadapter(A<T2> _a) { a = _a; } 
        public override event EventHandler  e
        {
            add { a.e += value; }
            remove { a.e -= value; }
        }
    }

Or chain it

class A<T>
{
    public event EventHandler e;

    protected void ChainEvent(object sender, EventArgs eventArgs)
    {
        e(sender, eventArgs);
    }
}
class Aadapter<T1, T2> : A<T1> 
{ 
    A<T2> a; 
    Aadapter(A<T2> _a)
    {
        a = _a;
        a.e += ChainEvent;
    }
}
Neil
  • 1,605
  • 10
  • 15
  • As I said, this is one approach but one in which I'd rather not take. (There are also known issues with virtual events in C# besides the extra overhead) – Stretto Jan 13 '11 at 05:13
  • @Stretto What is the overhead associated with a virtual event? – Jay Jan 13 '11 at 05:20
  • You could also just chain it - this seems dirty, dirty dirty. Making it virtual shouldn't be a major overhead difference - class A { public event EventHandler e; protected void ChainEvent(object sender, EventArgs eventArgs) { e(sender, eventArgs); } } class Aadapter : A { A a; Aadapter(A _a) { a = _a; a.e += ChainEvent; } } – Neil Jan 13 '11 at 05:23
  • It's not as simple as that but basically that is one of the ways. You must subscribe from "both sides"(the adapter's base and the wrapped object). I've posted the example code for both cases so there is no doubt that there are 2 methods that work. – Stretto Jan 13 '11 at 05:30
  • Quite honestly - I would use the virtual event route and wait to have a performance issue. If this is really resource intensive - profile your app at that point and fix the slugs - but this kind of pre-optimization costs more in developer time than it saves in hardware costs most of the time. – Neil Jan 13 '11 at 05:43
0

Why is subscribing and forwarding events not pretty? I find it elegant.

Doing this is consistent with the way the rest of the adapter is implemented.

Even if you could use a pointer, it would be inconsistent because you wouldn't want to do that in every case.

For example, if you're adapting a class that implements INotifyPropertyChanged to an interface that does not, but exposes a couple of properties, such as "TitleChanged" and "MaxLengthChanged," you wouldn't then use a pointer. Your adapter would expose those two events, and consumers would subscribe. Your adapter would subscribe to the PropertyChanged event, and raise "TitleChanged" only when it gets notified that "Title" was modified, and "MaxLengthChanged" only when it gets notified that "MaxLength" was modified. All other notifications would be ignored.

I favour this approach as I find it straightforward, consistent and true to the pattern.

Jay
  • 56,361
  • 10
  • 99
  • 123
  • In this case this adapter is only adapting a generic class to itself. The class's generic type parameter is somewhat "loosely" bound in the sense it is not used much. What this means is that the class is almost independent of the generic parameter and almost needs no adapter. So in this context it would be more natural to tie the events from the adapter to wrapped object rather than subscribe to both. Obviously the "pointer method" isn't going to work and unless someone comes up with some better way I'm stuck with choosing from the two that do work. – Stretto Jan 13 '11 at 04:49
0

Example showing the "standard" methods to solve the problem. The first uses virtual events/methods while the second does a "doubled ended" forwarding scheme. Both have their pro's and cons but would be nice if there was an easier method that didn't grow with the number of events. What we would like to do is sort of combine the two events into one directly instead of indirectly which is what all this code does. (pointers would be such a method if they were possible in C#)

//#define __virtual
#define __direct

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



namespace VirtualEvents
{



    #if __virtual
    #region
    public class A<T> 
    { 
        public virtual event EventHandler e;

        public virtual void Fire() { e(this, null); }
    }

    public class Aadapter<T1, T2> : A<T1> 
    { 
        A<T2> a;


        public override event EventHandler e
        {
            add { a.e += new EventHandler(value); }
            remove { a.e -= new EventHandler(value); }
        }

        public override void Fire()
        {
            a.Fire();
        }

        public Aadapter(A<T2> _a) 
        { 
            a = _a; 
        } 
    }
    #endregion
    #elif __direct
    #region

    public delegate EventHandler EventHandlerPtr();
        public class eventPtr
        {
            public EventHandler _event;
        }
    public class A<T>
    {


        //internal EventHandler _event;
        public eventPtr _event = new eventPtr();

        public event EventHandler e
        {
            add { _event._event += value; }
            remove { _event._event -= value; }
        }

        public void Fire() { _event._event(this, null); }


    }

    public class Aadapter<T1, T2> : A<T1>
    {
        A<T2> a;

        public Aadapter(A<T2> _a)
        {
            a = _a;
            this._event = a._event;
        }
    }

    #endregion
    #else
    #region 
    public class A<T>
    {
        public event EventHandler e;

        public void Fire() { e(this, null); }
    }

    public class Aadapter<T1, T2> : A<T1>
    {
        A<T2> a;

        public Aadapter(A<T2> _a)
        {
            a = _a;

            a.e += new EventHandler(a_e);
            e += new EventHandler(Aadapter_e);
        }

        void Aadapter_e(object sender, EventArgs e)
        {
            a.e -= new EventHandler(a_e);
            a.Fire();
            a.e += new EventHandler(a_e);
        }

        void a_e(object sender, EventArgs e)
        {       
            this.e -= new EventHandler(Aadapter_e);
            Fire(); 
            this.e += new EventHandler(Aadapter_e);
        }
    }
    #endregion
    #endif


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

            var a = new A<double>();
            var q = new Aadapter<int, double>(a);

            a.e += new EventHandler(a_e);
            q.e += new EventHandler(q_e);


            a.Fire();
            q.Fire();
            ((A<int>)q).Fire();

            Console.ReadKey();
        }

        static void a_e(object sender, EventArgs e)
        {
            Console.WriteLine("From a");
        }

        static void q_e(object sender, EventArgs e)
        {
            Console.WriteLine("From q");
        }

    }
}

(edit: the code now includes a new method which wraps the event in a class which now allows events to be assigned easily and effectively represents the "the pointer" case. Hopefully someone can improve upon these even more.)

Stretto
  • 486
  • 2
  • 10
  • BTW, one method that would work is to roll your own event construct. e.g., use lists/hashtables and callbacks. In this case one could easily assign the callback list from wrapped object to a reference in the adapter class. I suppose one can still do this using the event semantics... I'll have to try that as it might be an decent solution. – Stretto Jan 13 '11 at 05:40