c#类实例通信

时间:2009-12-10 17:50:45

标签: c#

如果你有同一个类的不同实例,例如“FootballTeam”,并且你想让这个FootballTeam类的另一个实例知道发生了什么,那么最好的方法是什么此?

事件不会真正起作用我猜...

EG:

FootballTeam A = new FootballTeam();
FootballTeam B = new FootballTeam();

// now A needs to let B know about it's manager change
// manager is a property inside this class...

5 个答案:

答案 0 :(得分:4)

事件可以起作用:

FootballTeam A = new FootballTeam();
FootballTeam B = new FootballTeam();
A.ManagerChanged += B.OnOtherManagerChanged;

事件的定义 - FootballTeamOnManagerChanged属性更改值时调用其Manager方法:

class FootballTeam
{
    public event EventHandler ManagerChanged;

    protected virtual void OnManagerChanged(EventArgs e)
    {
        EventHandler handler = ManagerChanged;
        if (handler != null)
            handler(this, e);
    }

    public void OnOtherManagerChanged(object sender, EventArgs e)
    {
        FootballTeam otherTeam = (FootballTeam) sender;
        // A manager changed on a different FootballTeam instance
        //  ...do something here
    }
}

答案 1 :(得分:4)

这个问题有很多回复提供了如何使用事件(或观察者模式)解决此类问题的示例。虽然这些是适当的模式,但选择如何以及何时使用它们会对最终结果产生重大影响

在您的示例中,您描述了在其他实例中发生相关更改时需要通知的FootballTeam个实例。但是,您必须确定是否应该真正成为FootballTeam类的责任来监听和响应此类事件。 Single Responsibility Principle指出应该只有一个原因可以改变一个类。坚持这种设计理念通常可以更清晰地分离关注点和整体更好的代码。

此类设计可能会出现许多问题。

首先,让每个FootballTeam负责监听更改并对其进行响应很快就会出现问题。首先,FootballTeam个实例之间的直接(点对点)通信线路的数量随实例数的平方增长:n *(n-1)。有五个团队,你将有20个连接,有十个团队你有90个,有三十个团队,你有870个。

管理所有这些实例的事件订阅(和取消订阅)可能会导致令人困惑且无法维护的代码。此外,在每对团队之间进行事件订阅可能会影响垃圾收集 - 并导致潜在的泄漏 - 或者至少,对象停留在内存中的时间比需要的时间长得多。

所有实例在彼此订阅事件的设计中的另一个潜在问题是无限或循环事件链:A通知B,其更新自身并通知C,C更新自身并通知A,其更新自身并通知B ......无限的。或者直到你的堆栈空间不足。在这样的设计中,这可能是一个难以解决的问题,并且可能需要笨拙的状态管理来防止循环和递归。

另一种方法是创建一个单独的观察者类 - 让我们称之为FootballTeamObserver - 订阅所有FootballTeam个实例的更改事件,并负责必要时跨实例传播更改。因此FootballTeam仍然负责在发生有意义的更改时进行广播,FootballTeamObserver会响应通知。重要的是,只有FootballTeamObserver - the singleton pattern的实例 - 确保所有通知都存在中央处理站点。这既减少了事件订阅的数量(只有团队的数量),又分清了以干净的方式响应变更的责任。它还可以负责循环检测并确保更新链的长度有限。

答案 2 :(得分:1)

在.Net中执行此操作的典型方法是为其他对象感兴趣的操作定义事件。例如,您可以定义以下

public class FootballTeam { 
  private string _manager;
  public string Manager {
    get { return _manager; }
    set { 
      if ( ManagerChanged != null ) { 
        ManagerChanged(this,EventArgs.Empty);
      }
    }
  }
  public event EventHandler ManagerChanged;
}

理想情况下,你想要一个更安全的类型事件,但这里只有这么多空间

然后,您可以在其他FootballTeam实例中侦听此事件并响应该事件。

FootballTeam a = new FootballTeam();
FootballTeam b = new FootballTeam();
a.ManagerChanged += (sender, e) => {
  Console.WriteLine("A's manager changed");
};

答案 3 :(得分:1)

有几种不同的方式。您需要这样做的事实有时表明存在设计问题,当然实用主义必须发挥作用。

一种简单的方法是在FootballTeam类中拥有一个私有静态事件,该事件本身在ctor中订阅:

public class FootballTeam
{
    private static event EventHandler SomethingHappened;

    public FootballTeam()
    {
        SomethingHappened += this.HandleSomethingHappened;
    }

    public void DoSomething()
    {
        SomethingHappened(); //notifies all instances - including this one!
    }
}

为避免内存泄漏,请确保通过实现IDisposable来清理事件处理程序:

public class FootballTeam : IDisposable
{
    //...

    public void Dispose()
    {
        SomethingHappened -= this.HandleSomethingHappened;
        //release the reference to this instance so it can be GC'd
    }
}

答案 4 :(得分:0)

您可以使用Observer pattern。它允许对象“订阅”他们关心的事件。