Return a value from an Event -- is there a Good Practice for this?
Asked Answered
C

9

47

I'm doing a small multi-threaded app that uses asynchronous TCP sockets, but I will get to the point: I'm using a custom event to read a value from a form and the delegate used by the event returns a string when finished.

My question here is: is that correct? is it OK to return values from the events? or is there a better way to do this? (like using a simple delegate to the form to read the values)

Collayer answered 30/7, 2009 at 23:58 Comment(0)
M
44

It's often awkward to return values from events. In practice, I've found it much easier to include a writable property on a set of custom EventArgs that is passed to the event, and then checked after the event fires -- similar to Cancel property of the WinForms FormClosing event.

Mccray answered 31/7, 2009 at 0:10 Comment(2)
Could you please elaborate on include a writable property on a set of custom EventArgs that is passed to the event part?Histiocyte
@Histiocyte Make the EventArgument object custom. Create a property where the results live.Shelve
B
32

I don't think it's a good idea... events are basically multicast delegates, so there can be multiple handlers. Which return value will you take in that case ?

Bloomer answered 31/7, 2009 at 0:11 Comment(0)
P
24

I know this is ages after the post but thought of adding comment with code to explain Dustin Campbell answer for if someone else comes across this thread. I came across this post while trying to decide what would be best practice and this is what is meant by the answer.

Create your own custom event handler class

public class myCustomeEventArgs:EventArgs
{
    public bool DoOverride { get; set; }
    public string Variable1 { get; private set; }
    public string Variable2{ get; private set; }

    public myCustomeEventArgs(string variable1 , string variable2 )
    {
        DoOverride = false;
        Variable1 = variable1 ;
        Variables = variable2 ;
    }
}

So when you create your event delegate you use your created event args like this.

public delegate void myCustomeEventHandler(object sender, myCustomeEventArgs e);

And in the class raising the event you declare the event.

public event myCustomeEventHandler myCustomeEvent;

So when you trigger the event in your class the class that listens for the event you can just in the body of the event set e.DoOverride = true; as it will be declared in the class firing the event.

Fire event for example:

if(myCustomeEvent != null)
{
    var eventArgs = new myCustomeEventArgs("Some Variable", "Another Varaible");
    myCustomeEvent(this, eventArgs);
    //Here you can now with the return of the event work with the event args
    if(eventArgs.DoOverride)
    {
       //Do Something
    }
}
Paralytic answered 16/7, 2013 at 13:55 Comment(2)
How do you know when the event will "return"?Handspring
Events are run on the same thread that raises them. The code that runs myCustomeEvent(...) will pass control (run on the same thread) to all of the subscribed event handlers. Once all of their code has been run, then control will return to the instruction right after the call to myCusomeEvent(...). If there are no subscribers, or of there are subscribers that rebroadcast the event onto another thread, then all of the properties on the eventArgs will have their default value (false for the bool and null for the strings).Handout
G
19

Note: only the last event returns the result.

class Program
{
static event Func<string, bool> TheEvent;

    static void Main(string[] args)
    {
        TheEvent += new Func<string, bool>(Program_TheEvent);
        TheEvent +=new Func<string,bool>(Program_TheEvent2);
        TheEvent += new Func<string, bool>(Program_TheEvent3);
        var r = TheEvent("s"); //r == flase (Program_TheEvent3)
    }

    static bool Program_TheEvent(string arg)
    {
        return true;
    }

    static bool Program_TheEvent2(string arg)
    {
        return true;
    }

    static bool Program_TheEvent3(string arg)
    {
        return false;
    }        
}
Gabel answered 19/10, 2014 at 15:46 Comment(0)
B
16

The closest example I can think of is the FormClosing event in WinForms. It lets the form cancel the event by setting the eventArgs.Cancel property to true. For you to do something similar, you would define your own event args class with the return value as a property on that class. Then pass an event args object whenever you raise the event. Whoever raised the event can inspect the event args object for the return value. Others who are receiving the event can also inspect or change the event args object.

Update: I just ran across the AppDomain.AssemblyResolve event, and it appears to be an event that returns a value. It seems you just need to declare a delegate type that returns a value, and then define your event with that delegate type. I haven't tried creating my own event like this, though. One advantage to using a property on the event argument is that all subscribers to the event can see what previous subscribers have returned.

Brantley answered 31/7, 2009 at 0:9 Comment(1)
Note: you can use any delegate to define events, so it would also be fine to define an event like public event Func<string, string, bool> SomeEvent; which would be callable as bool r = SomeEvent("foo", "bar");Jagir
L
9

I don't know if this is best practice but i did it this way.

   Func<DataRow, bool> IsDataValid;

   // some other code ....

   isValid = true;
   if (IsDataValid != null)
   {
      foreach (Func<DataRow, bool> func in IsDataValid.GetInvocationList())
      {
         isValid &= func(Row);
      } 
   }
Lownecked answered 12/7, 2011 at 9:15 Comment(0)
S
1

If event returns a value and there are multiple handlers registered the event returns the result value of the last called handler. Look for an example at http://blogs.msdn.com/b/deviations/archive/2008/11/27/event-handlers-returning-values.aspx

Sedimentology answered 19/9, 2014 at 13:12 Comment(1)
Link in answer is dead - "Page not found".Tread
A
0

I looped over the properties of the EventArgs like this and pulled out its X and Y values.

private void navBarControl1_Click(object sender, EventArgs e)
{
    int _x = 0;
    int _y = 0;
    
    Type t = e.GetType();
    IList<PropertyInfo> props = new List<PropertyInfo>(t.GetProperties());

    foreach (PropertyInfo prop in props)
    {
        if (prop.Name == "X")
        {
            object propValue = prop.GetValue(e, null);
            _x = Convert.ToInt32(propValue);
        }
        if (prop.Name == "Y")
        {
            object propValue = prop.GetValue(e, null);
            _y = Convert.ToInt32(propValue);
        }
    }
Amoy answered 28/4, 2016 at 2:8 Comment(0)
C
-1
void method()
{
    list<string> strings = new list<string>();

    dostuff += stuff;
    dostuff += stuff;

    dostuff(this, new EventHandlerArgs(){ Parameter = strings })

    foreach(string currString in strings)
    {
          //....
    }
}

void stuff(object sender, EventHandlerArgs e)
{
    list<string> strings = e.Parameter as list<string>;

    if (strings != null)
    {
        strings.Add(MyString)
    }
}
Crystallization answered 13/12, 2011 at 16:16 Comment(1)
Welcome to stackoverflow! It's always better to provide a short description for a sample code to improve the post accuracy :)Tchao

© 2022 - 2024 — McMap. All rights reserved.