У меня есть шаблон адаптера для общего класса, который существенно адаптируется между типами:
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.
Я сомневаюсь, что есть простой способ, но, возможно, у кого-то есть идея заставить его работать.
(Кстати, я понимаю, что это возможно с виртуальными событиями, но я бы хотел избежать этого, если это вообще возможно)
Я думаю, что это то, что вам нужно:
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;
}
}
Пример, показывающий "стандартные" методы решения проблемы. Первый использует виртуальные события/методы, а второй - схему переадресации с удвоением конца. У обоих есть свои про и минусы, но было бы неплохо, если бы был более простой метод, который не увеличивался с количеством событий. То, что мы хотели бы сделать, - это объединить два события в одно прямо, а не косвенно - это то, что делает весь этот код. (указатели были бы таким методом, если бы они были возможны в С#)
//#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: теперь код включает новый метод, который переносит событие в класс, который теперь позволяет назначать события, легко и эффективно представляет собой случай "указатель". Надеюсь, кто-то может улучшить их еще больше.)
Почему события подписки и пересылки не очень хороши? Я нахожу это элегантным.
Выполнение этого согласовано с тем, как реализуется остальная часть адаптера.
Даже если вы могли бы использовать указатель, это было бы непоследовательно, потому что вы не хотели бы этого делать в каждом случае.
Например, если вы адаптируете класс, который реализует INotifyPropertyChanged
, к интерфейсу, который этого не делает, но предоставляет несколько свойств, таких как "TitleChanged" и "MaxLengthChanged", вы не должны использовать указатель, Ваш адаптер будет раскрывать эти два события, и потребители будут подписываться. Ваш адаптер будет подписаться на событие PropertyChanged
и поднять "TitleChanged" только тогда, когда он получит уведомление о том, что "Title" был изменен, и "MaxLengthChanged", только когда он получает уведомление о том, что "MaxLength" был изменен. Все остальные уведомления будут проигнорированы.
Я одобряю этот подход, поскольку считаю его простым, последовательным и верным шаблону.