Using EventArgs to pass information back to invoking class
Asked Answered
C

3

27

Is it frowned upon to modify EventArgs in event handlers for the purpose of passing information back to the class invoking the event?

For instance, if I have a low-level communication class needing to validate a certificate for SSL but it has no way of knowing what a valid certificate looks like since that is the knowledge of the different users of the class.

class ValidationEventArgs : System.EventArgs
{
    public X509Certificate Certificate { get; set; }
    public bool Valid { get; set; }
}

Then in the using objects they hook up to the event, and check it somehow changing the Valid flag to indicate if the certificate is acceptable or not.

comms.ValidationEvent += CertValidationHandler;
    
void CertValidationHandler(ValidationEventArgs args)
{
    if (args.Certificate.Issuer.Contains(COMPANY_NAME))
        args.Valid = true;
}

I have found references of EventArgs being used like this but I have also seen people saying it is not recommended.

Edit: Maybe I should clarify that this is not about inheriting EventArgs, but using them as a bi-directional channel of communication. As others commented this is acceptable and whatever noise Google picks up to the opposite is probably just people having misunderstood/misused the concept and now has the same personal crusade against as goto.

Craver answered 16/2, 2012 at 8:9 Comment(3)
Who says it's not recommended? This happens all the time in the framework. Look at CancelEventArgs; it doesn't get much more basic than that.Twobit
Googling around I got the feeling that having mutable EventArgs is not really something one should do, probably because someone has misused it at some point. ThanksCraver
Like may things it has a time and place to uses it, using it in the wrong way and place is bad, but that doesn't make it bad its self.Rutherfordium
B
47

Ask yourself the following question, "when I publish an event, do I want any subscriber to alter any of the EventArgs values"? If the answer is no, e.g. you are broadcasting readonly information then make the class immutable, however if you need some feedback from a subscriber then make the properties which need to be altered mutable.

To clarify, in a broadcast example, we want to tell any subscriber something but not let them alter the value.

public class ProgressEventArgs : EventArgs
{
    public ProgressEventArgs(int current)
    {
        this.Current = current;
    }

    public int Current { get; private set; }
}

Equally though, we could also raise an event to ask for information the class itself does not know.

public class FeedbackEventArgs : EventArgs
{
    public bool ShouldContinue { get; set; }
    public string Reason { get; set; }
}
Beekeeper answered 16/2, 2012 at 8:53 Comment(2)
I know this is old but I'd like to point out that one should be careful when using this approach as there may be more than one subscriber per event, but it would be the same event object being passed around so using simple setters could result in data being overwritten.Devaney
@Devaney good point! In the mutable example, it would be a case of "last write wins"Beekeeper
T
11

You can use the EventArgs class through the Generic Types approach. In this sample, i will use the Rect class with as return type:

public EventHandler<Rect> SizeRectChanged;

Raising the event:

if(SizeRectChanged != null){
   Rect r = new Rect(0,0,0,0);
   SizeRectChanged(this,r);
}

Listening the event:

anyElement.SizeRectChanged += OnSizeRectChanged;

public void OnSizeRectChanged(object sender, Rect e){
    //TODO abything using the Rect class
    e.Left = e.Top = e.Width = e.Height = 50;
}
Trictrac answered 13/11, 2013 at 10:43 Comment(2)
This won't work because EventHandler<TEventArgs> has the constraint of where TEventArgs : EventArgs. This means the generic type must be derived from EventArgs. Trying to use a Rect will result in a compiler error.Blowgun
The constraint for inheriting EventArgs has been dropped in .net 4.5.Salientian
W
2

I'm not aware of any recommendation against inheriting from EventArgs; as far as I know, inheriting from it is good practice.

As a general principle, I suggest that you make your derived classes immutable. That will make it much safer to pass between threads should you need to do so. The simplest way to do this is to declare your properties as { get; private set; }, and only set them in the constructor. Obviously you can't do that with the specific use case you have in the question, but you should do it where possible.

Winger answered 16/2, 2012 at 8:20 Comment(1)
yes, I oversimplified the example, the only mutable parameter would be the boolean. The question was using events to pass data back to the invoker, basically as a two-directional channel, not inheriting EventArgs. Only issue I can see would be with multiple listeners/threads, but the specification where this will be used does not allow for thatCraver

© 2022 - 2024 — McMap. All rights reserved.