Предположим, я хочу создать набор наблюдателей на основе типа. То есть, когда они уведомляются о событии, им сообщается тип одного из аргументов, а затем они решают, действовать или нет, в зависимости от того, может ли он работать с этим типом.
Есть какие-нибудь простые способы сделать это? Я подумал, что это будет довольно просто сделать с дженериками, но, похоже, это оказалось сложнее, чем я предполагал. И я бы предпочел не иметь дело с приведением кучи ссылок на объект, если я могу этого избежать.
Я застрял в этом:
public delegate void NotifyDelegate<T>(IEnumerator<T> loadable, NotifyArgs na);
interface IObserver
{
void Notify<T>(IEnumerator<T> loadable, NotifyArgs na);
}
class Subject
{
NotifyDelegate notifier; //won't compile: needs type args
void Register(IObserver o)
{
notifier += o.Notify;
}
}
Конечно, я мог бы сделать Subject универсальным, но тогда у меня должен быть отдельный Subject для каждого типа. Есть ли у кого-нибудь здесь совет? Есть ли какая-то часть функциональности, которую мне где-то не хватает, или я слишком усложняю это?
ОБНОВИТЬ: Я слишком упростил аргументы, которые принимают Notify и NotifyDelegate. Вместо этого:
public delegate void NotifyDelegate<T>(NotifyArgs na);
На самом деле я хочу сделать что-то вроде этого:
public delegate void NotifyDelegate<T>(IEnumerator<T> loadable, NotifyArgs na);
В основном я пытаюсь передавать туда и обратно данные из базы данных. Извините, если предыдущий пример кода кого-то смутил.





Во-первых, измените код, который у вас есть, на следующий:
interface IObserver
{
}
class Subject
{
public Subject ()
{
m_observers = new List<IObserver> ();
}
public void Register (IObserver o)
{
m_observers.Add (o);
}
List<IObserver>
m_observers;
}
Затем используйте отражение, чтобы найти подходящую функцию на основе типа параметра:
public void NotifyObservers (object param)
{
foreach (IObserver observer in m_observers)
{
foreach (MethodInfo method in observer.GetType ().GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance))
{
if (method.Name == "Notify")
{
ParameterInfo []
parameters = method.GetParameters ();
if (parameters.Length == 1 && parameters [0].ParameterType == param.GetType ())
{
method.Invoke (observer, new object [] { param });
break;
}
}
}
}
}
и используйте это так:
class Observer : IObserver
{
public Observer (Subject s)
{
s.Register (this);
}
void Notify (float value)
{
System.Diagnostics.Trace.WriteLine ("float value = " + value);
}
void Notify (int value)
{
System.Diagnostics.Trace.WriteLine ("int value = " + value);
}
}
static void Main (string [] args)
{
Subject
s = new Subject ();
Observer
o = new Observer (s);
float
v1 = 3.14f;
int
v2 = 42;
System.Diagnostics.Trace.WriteLine ("sending float");
s.NotifyObservers (v1);
System.Diagnostics.Trace.WriteLine ("sending int");
s.NotifyObservers (v2);
}
interface IObserver
{
void Notify(NotifyArgs na);
bool SupportsType(Type t);
}
class Subject
{
List<IObserver> observers;
void Register(IObserver o)
{ observers.Add(o);
}
void OnNotify(Type t, NotifyArgs args)
{
foreach (IObserver o in observers)
{
if (o.SupportsType(t)) o.Notify(args));
}
}
}
Интересная идея. Это немного сложнее, чем я хочу реализовать, но, по крайней мере, похоже, что это сработает, если я не смогу найти более простой способ.