简体   繁体   中英

c# class instance communication

When you have different instances of the same class such as ' FootballTeam ' for example and you want to let another instance of this FootballTeam class know that something occured, whats the best way to go about this?

Events won't really work I guess...

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...

Events could work:

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

Definition of the event -- FootballTeam calls its OnManagerChanged method when its Manager property changes value:

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

There are a lot of responses to this question that provide examples of how to solve this type of problem using events (or the Observer pattern). And while these are appropriate patterns to employ, choosing how and when to use them can have a significant impact on the end result .

In your example, you describe FootballTeam instances that need to be informed when relevant changes in other instances take place. However, you must decide whether it should really be the responsibility of the FootballTeam class to both listen for and respond to such events. The Single Responsibility Principle states that there should be only one reason for a class to change. Adhering to this design concept generally results in clearer separation of concerns and overall better code.

There are a number of problems that can arise in such a design.

First, having each FootballTeam be responsible for listening for changes and responding to them can quickly become problematic. For one thing, the number of direct (point-to-point) communication lines between FootballTeam instances grows with the square of the number of instances: n*(n-1). With five teams you would have 20 connections, with ten teams you would have 90, with thirty teams you would have 870.

Managing the event subscriptions (and unsubscription) for all these instances is likely to result in confusing and unmaintainable code. Also, having event subscriptions between each pair of teams can have affect garbage collection - and result in potential leaks - or in the very least, instances of objects staying in memory much longer than needed.

Another potential problem with a design where all instances subscribe to events on each other, is infinite or cyclical event chains: A notifies B, which updates itself and notifies C, which updates itself and notifies A, which updates itself and notifies B ... ad infinitum. Or until you run out of stack space. This can be a difficult problem to solve in such a design, and would likely require awkward state management to prevent cycles and recursion.

An alternative approach, is to create a separate observer class - let's call it FootballTeamObserver - that subscribes to the change events of all FootballTeam instances, and would responsible for propagating changes, as necessary, across instances. So FootballTeam would still be responsible for broadcasting when a meaningful change takes place, and FootballTeamObserver would respond to the notification. Importantly, there would ever only be once instance of FootballTeamObserver - the singleton pattern - ensuring that a central handling site exists for all notification. This both reduces the number of event subscriptions (which would only be as many as there are teams) and separates the responsibility of responding to changes in a clean way. It can also be responsible for cycle detection and making sure that update chains have a finite length.

The typical way to do this in .Net is to define an event for operations that are interesting to other objects. For instance you might define the following

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

Ideally you'd want a more type safe event but there's only so much space here

You can then listen to this event in other FootballTeam instances and respond to that event.

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

There are a few different ways. The fact that you need to do this is sometimes indicative of a design problem, though of course pragmatism must come into play.

One simple way is to have a private static event in the FootballTeam class which itself subscribes to in the ctor:

public class FootballTeam
{
    private static event EventHandler SomethingHappened;

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

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

To avoid a memory leak, make sure to clean up the event handlers by implementing IDisposable:

public class FootballTeam : IDisposable
{
    //...

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

You could use the Observer pattern . It allows objects to "subscribe" to events that they care about.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM