Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Static events on non-static classes

Tags:

c#

events

There are situations where I'm quite fond of static events, but the fact that I rarely see them in other people's code makes me wonder if I'm missing something important. I found a lot of discussions about static events on this site, but most of them deal with situations that I'm not interested in (like on static classes) or where I wouldn't think of using them in the first place.

What I am interested in are situations where I might have many instances of something and a single instance of a long-living "manager" object that reacts to something on those instances. A very simple example to illustrate what I mean:

public class God {

    //the list of followers is really big and changes all the time, 
    //it seems like a waste of time to
    //register/unregister events for each and every one...  
    readonly List<Believer> Believers = new List<Believer>();

    God() {
        //...so instead let's have a static event and listen to that
        Believer.Prayed += this.Believer_Prayed;
    }

    void Believer_Prayed(Believer believer, string prayer) {
        //whatever
    }
}

public class Believer {

    public static event Action<Believer, string> Prayed;

    void Pray() {
        if (Prayed != null) {
            Prayed(this, "can i have stuff, please");
        }
    }
}

To me, this looks like a much cleaner and simpler solution than having an instance event and I don't have to monitor changes in the believers collection either. In cases where the Believer class can "see" the God-type class, I might sometimes use a NotifyGodOfPrayer()-method instead (which was the preferred answer in a few similar questions), but often the Believer-type class is in a "Models"-assembly where I can't or don't want to access the God class directly.

Are there any actual downsides to this approach?

Edit: Thanks to everyone who has already taken the time to answer. My example may be bad, so I would like to clarify my question:

If I use this kind of static events in situations, where

  • I'm sure there will only ever be one instance of the subscriber-object
  • that is guaranteed to exist as long as the application is running
  • and the number of instances I'm watching is huge

then are there potential problems with this approach that I'm not aware of?

Unless the answer to that question is "yes", I'm not really looking for alternative implementations, though I really appreciate everyone trying to be helpful. I'm not looking for the most pretty solution (I'd have to give that prize to my own version simply for being short and easy to read and maintain :)

like image 271
wilford Avatar asked Feb 14 '14 11:02

wilford


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C language?

C is a structured, procedural programming language that has been widely used both for operating systems and applications and that has had a wide following in the academic community. Many versions of UNIX-based operating systems are written in C.


1 Answers

One important thing to know about events is that they cause objects which are hooked to an event not to be garbage collected until event owner is garbage collected, or until event handler is unhooked.

To put it into your example, if you had a polytheistic pantheon with many gods, where you promoted and demoted gods such as

new God("Svarog");
new God("Svantevit");
new God("Perun");

gods would remain in your RAM while they are attached to Believer.Prayed. This would cause your application to leak gods.


I'll comment on design decision also, but I understand that example you made is maybe not best copy of your real scenario.

It seems more reasonable to me not to create dependency from God to Believer, and to use events. Good approach would be to create an event aggregator which would stand between believers and gods. For example:

public interface IPrayerAggregator
{
    void Pray(Believer believer, string prayer);
    void RegisterGod(God god);
}

// god does
prayerAggregator.RegisterGod(this);
// believer does
prayerAggregator.Pray(this, "For the victory!");

Upon Pray method being called, event aggregator calls appropriate method of God class in turn. To manage references and avoid memory leaks, you could create UnregisterGod method or hold gods in collection of weak references such as

public class Priest : IPrayerAggregator
{
    private List<WeakReference> _gods;

    public void Pray(Believer believer, string prayer)
    {
        foreach (WeakReference godRef in _gods) {
            God god = godRef.Target as God;
            if (god != null)
                god.SomeonePrayed(believer, prayer);
            else
                _gods.Remove(godRef);
        }
    }

    public void RegisterGod(God god)
    {
        _gods.Add(new WeakReference(god, false));
    }
}

Quick tip: Temporarily store event delegate as listeners might unhook their event handlers

void Pray() {
    var handler = Prayed;
    if (handler != null) {
        handler(this, "can i have stuff, please");
    }
}

Edit

Having in mind details you added about your scenario (huge number of event invokers, constant and single event watcher) I think you chose right scenario, purely for efficiency reasons. It creates least memory and cpu overhead. I wouldn't take this approach generally, but for scenario you described static event is very pragmatic solution that I might take.

One downside I see is flow of control. If your event listener is created in single instance as you say, then I would leverage singleton (anti)pattern and invoke method of God from Believer directly.

God.Instance.Pray(this, "For the victory!");
//or
godInstance.Pray(this, "For the victory!");

Why? Because then you get more granular control over performing action of praying. If you decide down the line that you need to subclass Believer to a special kind that doesn't pray on certain days, then you would have control over this.

like image 68
Nikola Radosavljević Avatar answered Sep 18 '22 08:09

Nikola Radosavljević