62

I have a static class that I would like to raise an event as part of a try catch block within a static method of that class.

For example in this method I would like to raise a custom event in the catch.

public static void saveMyMessage(String message)
{
    try
    {
        //Do Database stuff
    }
    catch (Exception e)
        {
              //Raise custom event here
        }
}

Thank you.

Avitus
  • 15,640
  • 6
  • 43
  • 53

6 Answers6

123

Important: be very careful about subscribing to a static event from instances. Static-to-static is fine, but a subscription from a static event to an instance handler is a great (read: very dangerous) way to keep that instance alive forever. GC will see the link, and will not collect the instance unless you unsubscribe (or use something like a WeakReference).

The pattern for creating static events is the same as instance events, just with static:

public static event EventHandler SomeEvent;

To make life easier (re null checking), a useful trick here is to add a trivial handler:

public static event EventHandler SomeEvent = delegate {};

Then you can simply invoke it without the null-check:

SomeEvent(null, EventArgs.Empty);

Note that because delegate instances are immutable, and de-referencing is thread-safe, there is never a race condition here, and no need to lock... who-ever is subscribed when we de-reference gets invoked.

(adjust for your own event-args etc). This trick applies equally to instance events.

Arialdo Martini
  • 4,427
  • 3
  • 31
  • 42
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • When reading your answer, you stated, "a subscription from a static event to an instance handler is a great way to keep that instance alive forever", I'm assuming this won't always be optimal. If, say, I'm using a static class to store Form Settings while it's running, this won't be such a bad thing since it's static, and can't have multiple copies? (Novice programmer, forgive the bad terminology) – Josh Nov 14 '12 at 14:37
  • @Josh the "great way" was meant as "a really dangerous way" - I've clarified that. If you only have one subscription, it isn't a huge problem regardless of static vs instance. The dangerous part comes when every instance subscribes (say) to a static event, or an event on a long-lived object, and never unsubscribes. Hey presto: memory clog. – Marc Gravell Nov 14 '12 at 14:44
  • 2
    "subscription from a static event to an instance handler" is it written like that on purpose? I believe that it should be "subscription from an instance handler a static event" instead. – Ondrej Janacek Jul 25 '14 at 17:06
  • 1
    @Ondrej yes, very deliberate: the event references the object, not the other way around. The direction (in terms of lifetime) is: from event to object – Marc Gravell Jul 25 '14 at 18:52
  • @MarcGravell Ok, I think it's the 'subscription from event' here what makes it sound strange to me, as event does not subscribe to anyone. They hold/store/... that subscription, however. Anyway, great answer. – Ondrej Janacek Jul 26 '14 at 13:43
  • I have no idea how I never thought of fixing the issue of null checking like that. – KDecker Sep 16 '16 at 16:27
19

Your event would also need to be static:

public class ErrorEventArgs : EventArgs
{
    private Exception error;
    private string message;

    public ErrorEventArgs(Exception ex, string msg)
    {
        error = ex;
        message = msg;
    }

    public Exception Error
    {
        get { return error; }
    }

    public string Message 
    {
        get { return message; }
    }
}

public static class Service
{
    public static EventHandler<ErrorEventArgs> OnError;

    public static void SaveMyMessage(String message)
    {
            EventHandler<ErrorEventArgs> errorEvent = OnError;
        if (errorEvent != null)
        {
            errorEvent(null, new ErrorEventArgs(null, message));
        }
    }
}

And Usage:

public class Test
{
   public void OnError(object sender, ErrorEventArgs args)
   {
        Console.WriteLine(args.Message);
   }
 }

 Test t = new Test();
 Service.OnError += t.OnError;
 Service.SaveMyMessage("Test message");
Todd
  • 5,017
  • 1
  • 25
  • 16
  • +1 it's been so long since I've had to write event handling I couldn't even remember the syntax for it. Apparently because I could only think of the 2.0 syntax (last time i had to write one) and forgot they added this in 3.5 – Chris Marisic Feb 04 '11 at 18:37
6

Several folks have offered up code examples, just don't fire an event using code such as:

if(null != ExampleEvent)
{
  ExampleEvent(/* put parameters here, for events: sender, eventArgs */);
}

as this contains a race condition between when you check the event for null and when you actually fire the event. Instead use a simple variation:

MyEvent exampleEventCopy = ExampleEvent;
if(null != exampleEventCopy)
{
  exampleEventCopy(/* put parameters here, for events: sender, eventArgs */);
}

This will copy any event subscribers into the exampleEventCopy, which you can then use as a local-only version of the public event without having to worry about any race conditions (Essentially, it is possible that another thread could pre-empt you right after you have checked the public event for null and proceed to remove all subscribers from the event, causing the subsequent firing of the event to throw an exception, by using a local-only copy, you avoid the possibility of another thread removing subscribers, since there is no way they could access the local variable).

Brian B.
  • 1,779
  • 11
  • 10
  • 7
    An easier fix is: public static event EventHandler Work = delegate { }; Now it is never null and you can just invoke it. Slightly lazy, but not enough to hurt. – Marc Gravell Nov 14 '08 at 06:25
  • 1
    @Mark Compared to copying the list of delegates and doing a null check, I think having one "do nothing" delegate is faster and simpler. Thanks for the answer above. – MindJuice Jul 22 '14 at 14:37
0

Just to add "Delegates are immutable" So, as shown in the example above the following line obtains a copy of the delegate.

EventHandler<ErrorEventArgs> errorEvent = OnError;
0

Note: VS2008, C#

Just declare an event as you normally would within the static class, but be sure to mark the event as static:

public static event EventHandler Work;

Then just subscribe to it as you normally would.

  • Re "as you normally would" - you need to be more cautious with static events, especially re unsubscribing. That will work in any version of C# btw. – Marc Gravell Nov 14 '08 at 06:27
0

The way I did this is the following:

1- define a delegate (this will enable you to have customized arguments):

public delegate void CustomeEventHandler(string str);

2- define an event based on the previously defined delegate:

public static event CustomeEventHandler ReadLine;

3- create an event handler:

static void OnLineRead(string currentLine)
        {
            if (ReadLine != null)
                ReadLine(currentLine);
        }

4- raise your event using the event handler (just call it wherever you want the event to be raised).

Hani
  • 753
  • 8
  • 10