Могу ли я сделать время жизни объекта зависимым от другого объекта без создания сильной связи в C #? - PullRequest
3 голосов
/ 08 сентября 2011

У меня есть некоторый заводской код в соответствии с:

public MyHandlingClass Create()
{ 
  var myHandler = new MyHandlingClass();
  var myNotifier = new MyNotifyingClass();
  myNotifier.Notify += myHandler.HandleNotification;

  return myHandler;
}

Идея состояла в том, чтобы позволить MyHandlingClass реагировать на внешние сигналы, не заботясь о типе отправителя.В различных приложениях фабричный класс, содержащий вышеупомянутый метод, может затем заменить MyNotifyingClass чем-то другим, способным вызвать событие аналогичного типа.

В реальном коде MyNotifyingClass обрабатывает события из неуправляемой DLL:

public class MyNotifyingClass
{
    private EventHandler notify;
    private UnmanagedEventHandler unmanagedNotify;

    public event EventHandler Notify
    {
        add
        {
            this.notify += value;

            // Hold on to a strong reference to the delegate passed into the
            // unmanaged DLL, to avoid garbage collection of the delegate:
            this.unmanagedNotify = HandleUnmanagedNotify;
            NativeMethods.SetNotifyCallback(this.unmanagedNotify);
        }
        remove
        {
            this.notify -= value;
        }
    }

    private void HandleUnmanagedNotify(IntPtr sender, IntPtr eventArgs)
    {
        this.notify(this, eventArgs.Empty);
    }
}

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

Я хотел бы каким-то образом принудительно установить, что экземпляр myNotifier имеет то же время жизни, что и экземпляр myHandler.Но мне не особо нравится идея позволить MyNotifierClass держаться за сильную ссылку на объект-обработчик.В конце концов, он больше не будет использоваться для реального объекта после того, как проводка будет на месте ...

Могу ли я каким-то образом создать прочные отношения между двумя классами и при этом заставить их не знать о существовании друг друга?

(РЕДАКТИРОВАТЬ: Короче говоря, этот фрагмент кода, похоже, воспроизводит мою проблему:)

[TestClass]
public class MyTests
{
  public class MyHandler
  {
    public void Handle(object sender, EventArgs e)
    { }
  }

  public class MyNotifier
  {
    public event EventHandler Notify;
  }

  [TestMethod]
  public void TestsSomeCondition()
  {
    var myHandler = new MyHandler();
    var myNotifier = new MyNotifier();

    myNotifier.Notify += myHandler.Handle;

    var weakNotifierRef = new WeakReference(myNotifier);
    myNotifier = null;

    GC.Collect();

    Assert.IsTrue(weakNotifierRef.IsAlive);
  }
}

Ответы [ 2 ]

2 голосов
/ 08 сентября 2011

Когда вы присоединяетесь к событию, вы создаете делегата и передаете его источнику события. Делегат содержит ссылку на объект, который будет вызван при возникновении события. То есть ссылка на объект выглядит так: «источник имеет ссылку на цель» . Если вы выбрасываете источник, цели все равно.

Поскольку вы уже представляете фабрику, она должна поддерживать работу уведомителей, т. Е. Сохранять их в статическом списке или более сложных схемах повторного использования уведомителей и т. Д.

Как предлагается в комментарии, наблюдатель / наблюдаемая модель может быть вам интересна. Фабрика реализует IObservable<T>, потребитель IObserver<T>. Пока вы сохраняете наблюдаемую информацию, любые наблюдатели, на которые ссылаются, будут уведомлены. Если вы добавите Rx-Extensions к уравнению, вы получите богатый набор методов для работы с Observables. Они будут, например, обычно позволяют избавиться от наблюдения, удаляя ссылку, что у Observable есть Observer.

1 голос
/ 08 сентября 2011

Вы можете создать базовый класс для MyNotifyingClass, который является типом, хранящимся в MyHandlingClass:

class BaseNotifyingClass // or maybe an interface type
{
  // put event in here
}

class MyNotifyingClass : public BaseNotifyingClass
{
  // as before, without event
}

class MyHandlingClass
{
  public MyHandlingClass (BaseNotifyingClass notifier)
  {
     m_notifier = notifier;
     m_notifier.Notify += HandleNotification;
  }
  // etc...
}

class SomeFactory
{
  public MyHandlingClass Create()
  { 
    var myNotifier = new MyNotifyingClass();
    var myHandler = new MyHandlingClass(myNotifier);

    return myHandler;
  }
  // etc...
}

Это дает дополнительное преимущество инкапсуляции связи между двумя классами внутри самих классови фабрика становится тем, что предлагает название, сервисом для создания объектов.Это означает, что изменение способа связывания двух классов не требует изменения фабричного класса.

...