C # Наблюдатели на основе типа? - PullRequest
3 голосов
/ 17 ноября 2008

Предположим, я хочу создать набор наблюдателей на основе типа. То есть, когда им сообщают о событии, им сообщают тип одного из аргументов, а затем решает, следует ли действовать в зависимости от того, может ли он работать с этим типом.

Есть ли простые способы сделать это? Я подумал, что это будет довольно просто сделать с дженериками, но, похоже, это оказалось сложнее, чем я себе представлял. И я бы предпочел не иметь дело с приведением нескольких ссылок на объект, если я могу избежать этого.

То, где я застреваю, заключается в следующем:

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

Конечно, я мог бы также сделать Предмет общим, но тогда я должен был бы иметь отдельный Предмет для каждого типа. У кого-нибудь есть совет? Есть ли какая-то часть функциональности, которую я где-то упускаю или я это слишком усложняю?

ОБНОВЛЕНИЕ : Я упростил аргументы, которые принимают Notify и NotifyDelegate. Вместо этого:

public delegate void NotifyDelegate<T>(NotifyArgs na);

Я действительно хочу сделать что-то вроде этого:

public delegate void NotifyDelegate<T>(IEnumerator<T> loadable, NotifyArgs na);

То, что я в основном пытаюсь передать взад и вперед, это данные из базы данных. Извините, если предыдущий пример кода кого-то смутил.

Ответы [ 2 ]

3 голосов
/ 17 ноября 2008

Прежде всего, измените полученный код на следующий:

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);
}
2 голосов
/ 17 ноября 2008
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));
      }
    }
}
...