【问题标题】:Can I make an objects lifetime depend on another object without making a strong coupling in C#?我可以让一个对象的生命周期依赖于另一个对象而不在 C# 中进行强耦合吗?
【发布时间】:2011-09-08 09:09:08
【问题描述】:

我有一些工厂代码:

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 保持对处理程序对象的强引用的想法。毕竟,一旦接线到位,它对实际对象就没有进一步的用途了……

我能否以某种方式在两个班级之间建立牢固的关系,但仍然让他们不知道彼此的存在?

(编辑:长话短说,这段代码 sn-p 似乎重现了我的问题:)

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

【问题讨论】:

  • 也许弱事件可能会有所帮助:stackoverflow.com/questions/1089309/weak-events-in-net.
  • 注册事件已经在链接对象了。
  • @steven :这似乎是相反的问题。我的问题是侦听器让通知程序保持活动状态......
  • @jan :我本来是这么想的——但这里似乎不是这样。在我从工厂方法返回后,通知程序引用了其他对象,但没有人引用它 - 显然使它适合 GC...
  • @Steven :我相信这个问题与 WeakEvents 正好相反

标签: c# events garbage-collection


【解决方案1】:

当您附加到一个事件时,您正在创建一个委托并将其传递给事件源。委托持有对事件触发时要调用的对象的引用。也就是说,对象引用类似于“源对目标有引用”。如果你把源扔掉,目标就不管了。

由于您已经介绍了工厂,因此工厂的工作应该是让通知程序保持活动状态,即将它们保存在静态列表中或更详细的重用通知程序等方案中。

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

【讨论】:

  • “既然你已经介绍了工厂,工厂的工作应该是让通知者保持活力”太好了!
【解决方案2】:

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

这具有将两个类之间的链接封装在类本身中的额外好处,工厂成为顾名思义,一种用于创建对象的服务。这意味着更改两个类的链接方式不需要更改工厂类。

【讨论】:

  • 正如我最初写的那样,我不太喜欢从处理程序中添加对通知程序的强引用。一旦设置了布线,处理程序就不再使用通知程序实例(除了建立生命周期关系,我真的认为这不应该是处理程序类的责任)。对我来说,这似乎有点像违反 LoD...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多