我可以使对象的生命周期依赖于另一个对象而不在C#中建立强耦合吗?

时间:2011-09-08 09:09:09

标签: c# events garbage-collection

我有一些工厂代码:

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 个答案:

答案 0 :(得分:2)

当您附加到事件时,您正在创建委托并将其传递给事件源。委托持有对触发事件时要调用的对象的引用。这就是对象引用就像 “source有一个关于目标的引用” 。如果你抛弃了源,目标就不在乎了。

由于你已经介绍了工厂,所以工厂的工作应该是让通知者保持活力,即将它们保存在一个静态列表中,或者更复杂的重复使用通知程序等方案。

正如评论中所建议的那样,您可能会对观察者/可观察模式感兴趣。工厂实施IObservable<T>,即消费者IObserver<T>。只要您保持observable存活,任何引用的观察者都会收到通知。如果您将Rx-Extensions添加到等式中,您将获得一组丰富的方法来处理Observable。他们将例如通常允许处理观察,删除Observable有Observer的引用。

答案 1 :(得分:1)

您可以为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...
}

这样做的另一个好处是可以在类本身中封装两个类之间的链接,工厂就会成为名称所暗示的,即用于创建对象的服务。这意味着更改两个类的链接方式不需要更改工厂类。