I need to start a download of some html so I call void GetHTML in another class. When it's done I want to pass along what Event it should raise in the calling class. How could I do this?
So it would look something like this:
public class Stuff
{
public void GetHTML(string url, event to raise here)
{
//Do stuff then raise event
}
}
public class Other
{
public Other()
{
Stuff stuff = new Stuff();
stuff.GetHTML(someUrl, somehow sending info that HTML_Done should be called);
}
void HTML_Done(string result, Event e)
{
//Do stuff with the result since it's done
}
}
I realize I'm not super clear with what I want to do, I'd be glad to fill in any missing parts.
Thanks for any suggestions!
Event Subscription and Notification
public class Stuff
{
// Public Event to allow other classes to subscribe to.
public event EventHandler GetHtmlDone = delegate { };
public void GetHTML(string url)
{
//Do stuff
// Raise Event, which triggers all method subscribed to it!
this.GetHtmlDone(this, new EventArgs());
}
}
public class Other
{
public Other()
{
Stuff stuff = new Stuff();
// Subscribe to the event.
stuff.GetHtmlDone += new EventHandler(OnGetHtmlDone);
// Execute
stuff.GetHTML("someUrl");
}
void OnGetHtmlDone(object sender, EventArgs e)
{
//Do stuff with the result since it's done
}
}
Using this pattern allows many more subscribers.
You also do not tie the notifier, the Stuff
class, to the caller, the Other
class.
You either have subscribers or not, no difference to the Stuff
class.
The Stuff
class should not know about the subscriber, it should merely raise an event it exposes for subscription.
EDIT
As ctacke pointed out correctly in the comments, raising the event using this.GetHtmlDone(this, new EventArgs());
will cause an exception if nobody has subscribed.
I changed my code above to ensure the event can be raised safely at all times by initialising my eventhandler.
As I'm always using it (by raising it) I'm sure it is only good practise to always initialise what you are using.
I could have added a null check on the event handler but in my personal oppinion I do not agree with that having to be the responsibility of the stuff
class. I feel the event should always be raised as it is the "responsible" thing to do.
I found this thread on SO which kind of confirmed to me that it does not seem wrong to do so.
In addition I also run Code Analysis on that code to ensure I'm not breaking the CA1805 rule by initialising the EventHandler. CA1805 was not raised and no rules have been broken.
Using my car analogy from the comments, I believe not initializing the eventhandler and raising it all the time would be the same than saying "When turning a corner in your car only use your indicator if someone is watching and if not don't bother". You never know if anyone is watching, so you might as well make sure you always do it.
This is simply my personal preference. Anyone else, please do add the != null check at all times if that is how you prefere to do it.
Thank you so much for the comments ctacke and for pointing this out. I learned quite a lot from this.
I will have to go back to some of my projects now and update some code to make sure my libraries are not crashing if nobody subscribes to my events. I feel quite silly not ever having caught that in any of my Tests.
I'm not sure if this is what you're after, but you can use the built in Action
delegate type:
public class Stuff
{
public void GetHTML(string url, Action<string, Event> callback)
{
//Do stuff
//raise event
callback("result here", new Event());
}
}
stuff.GetHTML(someUrl, HTML_Done);
Alternatively, use the standard event pattern and the EventHandler<T>
delegate type. You'll need to create your own EventArgs
type.
You want a callback.
public class Stuff
{
public void GetHTML(string url, Action callback)
{
// Do stuff
// signal we're done
callback();
}
}
public class Other
{
public Other()
{
Stuff stuff = new Stuff();
// using a lambda callback
stuff.GetHTML(someUrl, ()=> Console.WriteLine("Done!") );
// passing a function directly
stuff.GetHTML(someUrl, MyCallback);
}
public void MyCallback()
{
Console.WriteLine("Done!");
}
}
If you want to pass arguments, define the Action appropriately, ie; Action<string>
, and then the callback becomes callback("some string")
The other option you have, is by using Events. It looks like that was what you were aiming for when you asked the question. I wouldn't recommend it, the callback option is nicer IMO. Though, for future reference, this is how you'd use an Event in this circumstance.
public delegate void DoneEventHandler(object sender, string result);
public class Stuff
{
public event DoneEventHandler DoneEvent = delegate {}; // avoid null check later
public void GetHtml(string url)
{
// do stuff
DoneEvent(this, "result");
}
}
public class Other
{
public void SomeMethod()
{
Stuff stuff = new Stuff();
stuff.DoneEvent += OnDone;
stuff.GetHtml(someUrl)
}
public void OnDone(objects sender, string result)
{
Console.WriteLine(result);
}
}
Info on how to use Events in C#.
If I ve not understood you wrong.I think you should try this way
public class Stuff
{
public void GetHTML(string url, event to raise here)
{
//Do stuff and instead of raising event here
}
}
public class Other
{
public Other()
{
Stuff stuff = new Stuff();
stuff.GetHTML(someUrl, somehow sending info that HTML_Done should be called);
//Raise event here once that method is finished
}
void HTML_Done(string result, Event e)
{
//Do stuff with the result since it's done
}
}
You can try this approach
public class Stuff
{
public delegate void DownloadComplete("you can pass whatever info you like here");
public event DownloadComplete OnDownloadComplete;
public void GetHtml(string UrlToDownload)
{
//Download the data here
//After data download complete
if(OnDownloadComplete)
OnDownloadComplete("arguments to be passed");
}
}
public class Other
{
public Other()
{
Stuff myStuff=new Stuff();
myStuff.OnDownloadComplete+=new EventHandler(myStuff_OnDownloadComplete);
}
void myStuff_OnDownloadComplete("arguments will be passed here")
{
//Do your stuff
}
}