События + шаблон адаптера

2

У меня есть шаблон адаптера для общего класса, который существенно адаптируется между типами:

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

Проблема состоит в том, что A содержит событие. Я действительно хочу, чтобы все обработчики событий, назначенные адаптеру, попадали в a.

Было бы замечательно, если бы я мог назначить обработчик события обработчику событий адаптера, но это невозможно?

Идея здесь заключается в том, что A почти на самом деле просто A, но нам нужен способ их адаптации. Из-за того, как работает событие, я не могу как эффективно это сделать, кроме как вручную добавить два обработчика событий, и когда они будут вызваны, они "ретранслируют" на другое событие. Это не очень хорошо, и было бы гораздо приятнее, если бы у меня было что-то вроде

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; }  }

в некотором смысле мы имеем указатель на событие, которое мы можем назначить событию a2.

Я сомневаюсь, что есть простой способ, но, возможно, у кого-то есть идея заставить его работать.

(Кстати, я понимаю, что это возможно с виртуальными событиями, но я бы хотел избежать этого, если это вообще возможно)

Теги:
adapter

3 ответа

1

Я думаю, что это то, что вам нужно:

    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; }
        }
    }

Или цепочку

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;
    }
}
  • 0
    Как я уже сказал, это один из подходов, но я бы предпочел его не использовать. (Есть также известные проблемы с виртуальными событиями в C # помимо дополнительных затрат)
  • 0
    @Stretto Какие издержки связаны с виртуальным событием?
Показать ещё 4 комментария
0

Пример, показывающий "стандартные" методы решения проблемы. Первый использует виртуальные события/методы, а второй - схему переадресации с удвоением конца. У обоих есть свои про и минусы, но было бы неплохо, если бы был более простой метод, который не увеличивался с количеством событий. То, что мы хотели бы сделать, - это объединить два события в одно прямо, а не косвенно - это то, что делает весь этот код. (указатели были бы таким методом, если бы они были возможны в С#)

//#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: теперь код включает новый метод, который переносит событие в класс, который теперь позволяет назначать события, легко и эффективно представляет собой случай "указатель". Надеюсь, кто-то может улучшить их еще больше.)

  • 0
    Кстати, один из методов, который сработает, - это создать собственную конструкцию события. например, используйте списки / хэш-таблицы и обратные вызовы. В этом случае можно легко назначить список обратного вызова из обернутого объекта для ссылки в классе адаптера. Я предполагаю, что все еще можно сделать это, используя семантику события ... Я должен буду попробовать это, поскольку это могло бы быть приличным решением.
0

Почему события подписки и пересылки не очень хороши? Я нахожу это элегантным.

Выполнение этого согласовано с тем, как реализуется остальная часть адаптера.

Даже если вы могли бы использовать указатель, это было бы непоследовательно, потому что вы не хотели бы этого делать в каждом случае.

Например, если вы адаптируете класс, который реализует INotifyPropertyChanged, к интерфейсу, который этого не делает, но предоставляет несколько свойств, таких как "TitleChanged" и "MaxLengthChanged", вы не должны использовать указатель, Ваш адаптер будет раскрывать эти два события, и потребители будут подписываться. Ваш адаптер будет подписаться на событие PropertyChanged и поднять "TitleChanged" только тогда, когда он получит уведомление о том, что "Title" был изменен, и "MaxLengthChanged", только когда он получает уведомление о том, что "MaxLength" был изменен. Все остальные уведомления будут проигнорированы.

Я одобряю этот подход, поскольку считаю его простым, последовательным и верным шаблону.

  • 0
    В этом случае этот адаптер адаптирует только общий класс к себе. Параметр универсального типа класса несколько «слабо» ограничен в том смысле, что он мало используется. Это означает, что класс практически не зависит от универсального параметра и почти не нуждается в адаптере. Таким образом, в этом контексте было бы более естественно связать события от адаптера с обернутым объектом, а не подписываться на оба. Очевидно, что «метод указателя» не сработает, и если кто-то не придумает какой-то лучший способ, я застряну с выбором из двух, которые работают.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню