Reading event description and examples of msdn I can see a discrepancy in the way events are subscribed to. Sometimes event handlers are passed "as is" and other times they are passed by instantiating a delegate using the handler method e.g.
...
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
// Subscribe to the event using C# 2.0 syntax
pub.RaiseCustomEvent += HandleCustomEvent;
}
// Define what actions to take when the event is raised.
void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
}
}
vs
public delegate void EventHandler1(int i);
...
public class TestClass
{
public static void Delegate1Method(int i)
{
System.Console.WriteLine(i);
}
public static void Delegate2Method(string s)
{
System.Console.WriteLine(s);
}
static void Main()
{
PropertyEventsSample p = new PropertyEventsSample();
p.Event1 += new EventHandler1(TestClass.Delegate1Method);
p.RaiseEvent1(2);
...
}
}
Can someone please provide clarity on this?
Thanks.
Your first code sample is syntactic sugar for the second one.
This syntax (omitting the constructor) was introduced by C# 2.
Related
I'm in a situation where I have to use 3rd party library that contains a lot of events and is imho not very well written. It fires up events that I have to handle in my code, but I'm trying to abstract it away (to be able to unit test rest of my code dependent on that library) so I need an adapter. The problem is that some of the events are of delegate type that take ref parameters. Here's an example of how the 3rd party library looks like:
delegate void AdapteeEventHandler1(SpecificAdaptee sender, int a, int b);
delegate void AdapteeEventHandler2(SpecificAdaptee sender, ref int a); // problematic delegate
class SpecificAdaptee
{
public event AdapteeEventHandler1 Event1;
public event AdapteeEventHandler2 Event2; // problematic event
/// <summary>Exercise Event1</summary>
public void FireEvent1()
{
Event1?.Invoke(this, 1, 2);
}
/// <summary>Exercise Event2</summary>
public void FireEvent2()
{
int a = 42;
Event2?.Invoke(this, ref a);
}
}
To show how I am abstracting regular event taking list of parameters, it contains Event1 of type AdapteeEventHandler1. The problematic type is AdapteeEventHandler2, but let me show first how I am going about adapting the whole thing:
#region AdaptedEventArgs
class AdaptedEventArgs1 : EventArgs
{
public int A { get; set; }
public int B { get; set; }
}
class AdaptedEventArgs2 : EventArgs
{
public int A { get; set; }
}
#endregion
/// <summary>These represent an abstraction layer between SpecificAdaptee and our own code</summary>
class Adaptor
{
private readonly SpecificAdaptee _specificAdaptee;
/// <summary>Maintains relationship between the event triggered by SpecificAdaptee and the adapted event.</summary>
private readonly IAdaptedEventHandlerManager _adaptedEventHandlerManager;
public Adaptor(SpecificAdaptee specificAdaptee, IAdaptedEventHandlerManager adaptedEventHandlerManager)
{
_specificAdaptee = specificAdaptee;
_adaptedEventHandlerManager = adaptedEventHandlerManager;
}
#region Events
/// <summary>Adapts SpecificAdaptee.Event1</summary>
public event EventHandler<AdaptedEventArgs1> AdaptedEvent1
{
add
{
_specificAdaptee.Event1 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler1>(value,
(sender, a, b) => value.Invoke(this, new AdaptedEventArgs1 { A = a, B = b }));
}
remove
{
_specificAdaptee.Event1 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler1>(value);
}
}
/// <summary>Adapts SpecificAdaptee.Event2</summary>
public event EventHandler<AdaptedEventArgs2> AdaptedEvent2
{
add
{
/* !!! ERROR HERE !!! */
_specificAdaptee.Event2 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler2>(value,
(sender, a) => value.Invoke(this, new AdaptedEventArgs2 { A = a }));
}
remove
{
_specificAdaptee.Event2 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler2>(value);
}
}
#endregion
}
So what is happening here is that when I register an event handler to Adaptor.AdaptedEvent1 I am wrapping EventHandler<AdaptedEventArgs1> in AdapteeEventHandler1 and register it to SpecificAdaptee.Event1, also converting the AdaptedEventArgs1 to list of parameters required by AdapteeEventHandler1. This way user can register to events of Adaptor that will be fired when SpecificAdaptee fires its own events. Next I will post a program that exercises this but note that the problem is in AdaptedEvent2, where I would like to do things in an analogous manner, but I don't know how to deal with the ref parameter (there is a syntax error in add accessor of AdaptedEvent2.
Here is a console application exercising the project:
class Program
{
public static void Main(string[] args)
{
var specific = new SpecificAdaptee();
var adapter = new Adaptor(specific, new AdaptedEventHandlerManager());
adapter.AdaptedEvent1 += OnAdaptedEvent1;
adapter.AdaptedEvent2 += OnAdaptedEvent2;
specific.FireEvent1();
specific.FireEvent2();
Console.ReadLine();
}
private static void OnAdaptedEvent1(object sender, AdaptedEventArgs1 args)
{
Console.WriteLine($"{nameof(OnAdaptedEvent1)}({sender}, {args.A}, {args.B})");
}
private static void OnAdaptedEvent2(object sender, AdaptedEventArgs2 args)
{
Console.WriteLine($"{nameof(OnAdaptedEvent2)}({sender}, {args.A})");
}
}
So that's how it's supposed to work. I register to events of my Adaptor that I have in my code, and events get fired when the 3rd party library (SpecificAdaptee) fires its own events (here in this example, triggered by calling specific.FireEvent1() and 2).
For completeness, so you can try it yourself I include code for AdaptedEventHandlerManager that maps adapted event handlers to SpecificAdaptee's handlers, so I can register and unregister multiple event handlers like I normally would do:
interface IAdaptedEventHandlerManager
{
TSpecificEventHandler RegisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler,
TSpecificEventHandler specificEventHandler);
TSpecificEventHandler UnregisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler)
where TSpecificEventHandler : class;
}
class AdaptedEventHandlerManager : IAdaptedEventHandlerManager
{
/// <summary>
/// Remembers relation between the specific handler and general handler. Important when unsubscribing from
/// events. Key is the general event handler we are registering to events of this class. Value are specific
/// event handlers.
/// </summary>
private readonly Dictionary<object, List<object>> _eventHandlers =
new Dictionary<object, List<object>>();
public TSpecificEventHandler RegisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler,
TSpecificEventHandler specificEventHandler)
{
List<object> eventHandlerList;
if (!_eventHandlers.TryGetValue(adaptedEventHandler, out eventHandlerList))
{
eventHandlerList = new List<object> { specificEventHandler };
_eventHandlers.Add(adaptedEventHandler, eventHandlerList);
}
else
{
eventHandlerList.Add(specificEventHandler);
}
return specificEventHandler;
}
public TSpecificEventHandler UnregisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler)
where TSpecificEventHandler : class
{
List<object> eventHandlerList;
if (!_eventHandlers.TryGetValue(adaptedEventHandler, out eventHandlerList))
{
return null;
}
var eventHandler = eventHandlerList.FirstOrDefault();
if (eventHandler != null)
{
eventHandlerList.Remove(eventHandler);
}
if (!eventHandlerList.Any())
{
_eventHandlers.Remove(adaptedEventHandler);
}
return eventHandler as TSpecificEventHandler;
}
}
This basically remembers in a dictionary the adapted event handler, and the list of SpecificAdaptee's handlers.
So my question: is there a way to adapt events taking ref parameters without retracting to custom delegate type that takes a ref parameter, so I can use standard EventHandler<> class with custom EventArgs descendant?
I realise it's quite a handful of code so please let me know if something is not clear. Thanks in advance.
ref parameter in the event is meant to set from the subscribers. Though it's a bad idea, the api which you're using works based on that.
You can take all the pain in the adapter class and make it work such that consumers are not polluted by the ref parameter. They can continue to use EventArgs style events.
public event EventHandler<AdaptedEventArgs2> AdaptedEvent2
{
add
{
_specificAdaptee.Event2 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler2>(value,
(SpecificAdaptee sender, ref int a) =>
{
var args = new AdaptedEventArgs2 { A = a };
value.Invoke(this, args);
a = args.A;
});
}
remove
{
_specificAdaptee.Event2 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler2>(value);
}
}
After the event is executed, we set the value of A to the ref parameter a. This simulates the behavior of ref parameter and also abstracts it under the adapter class. If A is changed in the event handler, it will be reflected in the SpecificAdaptee class too.
To show how this works like a ref parameter:
class SpecificAdaptee
{
...
public void FireEvent2()
{
int a = 42;
if (Event2 != null)
Event2(this, ref a);
Console.WriteLine("A value after the event is {0}", a);
}
}
private static void OnAdaptedEvent2(object sender, AdaptedEventArgs2 args)
{
Console.WriteLine($"{nameof(OnAdaptedEvent2)}({sender}, {args.A})");
args.A = 15;
}
This prints:
A value after the event is 15
PS: For brevity I've added only the parts of your program which needs a change.
I'm used to using delegate EventHandler for event callbacks but now I'm attempting to use event Action for invoking events. I couldn't find much info on how this can be used properly anywhere so I'm hoping someone can point me in the right direction.
I have an Action event handler that handles string objects. In my subscriber class I have public event Action<string> UploadProgress;. The event handler is invoked like this:
UploadProgress.Invoke(string.Format("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / totalToUpload));
The listener class is subscribed to this event as below:
uploadFile.UploadProgress += uploadFile_UploadProgress;
void uploadFile_UploadProgress(string obj)
{
var prog = obj;
}
When the event is invoked, I get System.NullReferenceException: Object reference not set to an instance of an object. I'm not sure what I need to change in the subscriber class to avoid this error. Can someone tell me the proper way to use event Action or provide me the link to where I can read up on it? I know how to use the normal Action but confused about declaring it as an event. Any help is appreciated.
This way is much better, send bytesToUpload and totalToUpload through event, instead of the whole Action (right sample):
internal class Program
{
private static void Main(string[] args)
{
SomeClass someClass = new SomeClass();
someClass.UploadProgress += SomeClass_UploadProgress;
someClass.DoSomeUpload();
}
private static void SomeClass_UploadProgress(object sender, UploadEventArgs e)
{
string s = string.Format("sending file data {0:0.000}%", (e.BytesSoFar * 100.0f) / e.TotalToUpload);
Console.WriteLine(s);
}
}
public class UploadEventArgs : EventArgs
{
public float BytesSoFar { get; set; }
public float TotalToUpload { get; set; }
}
public class SomeClass
{
public event EventHandler<UploadEventArgs> UploadProgress;
public void DoSomeUpload()
{
if (UploadProgress != null)
{
UploadEventArgs e = new UploadEventArgs();
e.BytesSoFar = 123f;
e.TotalToUpload = 100000f;
UploadProgress.Invoke(this, e);
}
}
}
Can anybody please tell me what is happening in below code in simple English, specifically around the usage of => and += symbols:
var ls = new LibraryServiceClient(AppSettings.Get("LibraryServiceBaseAddress"),
SessionId, App.Id, _user.UUID);
ls.MakingRequest += (s, e) =>
{
LogStash.LogDebug("Library Service | Before making request : {0}",
DateTime.UtcNow.ToString("HH:mm:ss.fff"));
};
You assign a new delegate to the event:
ls.MakingRequest +=
You create a lambda expression, a function having two parameters, s and e:
(s, e) =>
Where the action of the lambda expression is:
{ LogStash.LogDebug("Library Service | Before making request : {0}", DateTime.UtcNow.ToString("HH:mm:ss.fff"));
(s,e) => { /*expresion*/ }
is a lambda function.
It's type is Action<object, EventArgs>.
ls.MakingRequest
is an event.
With += you register a handler to this event.
When the event is fired all registered handlers will execute.
A handler has the same signature as the action - it takes an object sender and an EventArgs eventArgs and returns void.
Thus, the lambda function type is compatible, so it will be called when the event is fired.
It is a syntactical sugar to make chained extension methods look more readable.
Below code will explain its evolution:
public class Program
{
public void Main(string[] args)
{
// named delegate
Tasker t = new Tasker();
t.doer += DoProvider.DoThis;
t.CallDoer("I am doing something");
// anonymous method
Tasker t2 = new Tasker();
t2.doer += delegate(string s){
Console.WriteLine (s);
};
t2.CallDoer("I am doing something again");
// syntactical sugar over anonymous methods aka lambda expressions
Tasker t3 = new Tasker();
t3.doer += (s)=>{
Console.WriteLine (s);
};
t3.CallDoer("I am doing yet another thing");
}
}
public delegate void DoSomething(string Foo);
public class Tasker
{
public event DoSomething doer;
public void CallDoer(string s)
{
doer.Invoke(s);
}
}
public static class DoProvider
{
public static void DoThis(string Bar)
{
Console.WriteLine (Bar);
}
}
I'm very new to programming, but I'm pretty sure I've almost got this Events thing down. I just need a few pointers, I guess. I'm trying to create a simple example of an Event, one that simply says "Hello!" every second or so. First I have my "Publisher" class:
public class Publisher
{
public delegate void MyEventHandler();
public event MyEventHandler MyEvent;
public void MyMethod()
{
while (true) { Thread.Sleep(1000); if (MyEvent != null) { MyEvent(); } }
}
}
Next, I have my "Subscriber" class:
public class Subscriber
{
public void Subscribe(Publisher TheObject)
{
TheObject.MyEvent += new Publisher.MyEventHandler(SayHello);
}
public void SayHello() { Console.WriteLine("Hello!"); }
}
Finally, in my Program:
public class Program {
static void Main(string[] args) {
var ThePublisher = new Publisher();
var TheSubscriber = new Subscriber();
TheSubscriber.Subscribe(ThePublisher);
ThePublisher.MyEvent();
}
}
The compiler says that I have an error though. It put a red line underneath "MyEvent" in "ThePublisher.MyEvent();" in Program. The error says "The event 'NameOfProject.Publisher.MyEvent' can only appear on the left hand side of += or -= (except when used from within the type 'NameOfProject.Publisher')" However, I know that I put in "+=" and nothing else. It is stated in my Subscriber class. What am I missing? What am I doing wrong?
In the last line, you're calling ThePublisher.MyEvent() instead of ThePublisher.MyMethod(), as you probably intended to do.
You may not trigger an event directly from outside the class which defines the event.
I want to send an alert message to any subscribers when a trap occurred.
The code I created works fine using a delegate method myDelegate del.
My questions are:
I want to know whether it's better to use EventHandler instead of a delegate?
I'm not sure what the differences are between a delegate and an EventHandler in my case.
notify(trapinfo t), that's what I've done here to get trap information. But it seems not to be a good idea. I read some online tutorial lesson introducing passing delegate object; I'm wondering if it's appropriate in my case? And how should I do it? Any suggestions?
Thanks a lot :)
My code:
public class trapinfo
{
public string info;
public string ip;
public string cause;
}
public class trap
{
public delegate void myDelegate(trapinfo t);
public myDelegate del;
trapinfo info = new trapinfo();
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
foreach (myDelegate d in del.GetInvocationList())
{
info.cause = "Shut Down";
info.ip = "192.168.0.1";
info.info = "Test";
d.Invoke(info);
}
}
}
}
public class machine
{
private int _occuredtime=0;
public trapinfo info = new trapinfo();
public void notify(trapinfo t)
{
++_occuredtime;
info.cause = t.cause;
info.info = t.info;
info.ip = t.ip;
getInfo();
}
public void subscribe(trap t)
{
t.del += new trap.myDelegate(notify);
}
public void getInfo()
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
info.cause, info.info, info.ip,_occuredtime);
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
machineA.subscribe(t);
t.run();
}
}
Update 2013-08-12
How about the observer/observable design pattern, that looks great in my case (EventHandler).
In my case, a machine subscribes to a trap messenger. (Add a machine to an invocation list)
Once a trap occurred, I send a message to all machines which are subscribed. (Call HandleEvent to handle it)
Advantages:
don't care about GetInvocationList() anymore, just use (+=) and (-=) to decide whom to send the trap.
It's easier to understand the logic of my program.
I know there are several ways to do it, but I wish I could analyze its pros and cons.
And thanks for your comments and suggestions, that would be very helpful!
I read the MSDN EventArgs article which Matthew Watson suggested.
Here's my Event Version:
public class TrapInfoEventArgs : EventArgs
{
public int info { get; set; }
public string ip { get; set; }
public string cause { get; set; }
}
public class trap
{
public event EventHandler<TrapInfoEventArgs> TrapOccurred;
protected virtual void OnTrapOccurred(TrapInfoEventArgs e)
{
EventHandler<TrapInfoEventArgs> handler = TrapOccurred;
if (handler != null)
{
handler(this, e);
}
}
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
TrapInfoEventArgs args = new TrapInfoEventArgs();
args.cause = "Shut Down";
OnTrapOccurred(args);
}
}
}
public class machine
{
public void c_TrapOccurred(object sender, TrapInfoEventArgs e)
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
e.cause, e.info, e.ip, DateTime.Now.ToString());
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
t.TrapOccurred += machineA.c_TrapOccurred; //notify machine A
t.run();
}
}
The difference between event and delegate is that:
event declaration adds a layer of protection on the delegate instance.
This protection prevents clients of the delegate from resetting the
delegate and its invocation list, and only allows adding or removing
targets from the invocation list
See What are the differences between delegates and events?
2) As I see it, your subscriber should not change delegates freely. One subscriber can assign = to it instead of adding +=. This will assign a new delegate, therefore, the previous delegate with its invocation list will be lost and previous subscribers will not be called anymore. So you should use Event for sure. Or you can change your code to make your delegate private and write additional functions for manipulating it to define your own event behavior.
//preventing direct assignment
private myDelegate del ;
public void AddCallback(myDelegate m){
del += m;
}
public void RemoveCallback(myDelegate m){
del -= m;
}
//or
public static trap operator +(trap x,myDelegate m){
x.AddCallback(m);
return x;
}
public static trap operator -(trap x, myDelegate m)
{
x.RemoveCallback(m);
return x;
}
//usage
//t.AddCallback(new trap.myDelegate(notify));
t+=new trap.myDelegate(notify);
It is much better to use an event for your example.
An event is understood by the Visual Studio Form and WPF designers, so you can use the IDE to subscribe to events.
When raising events, there is no need for you to write your own foreach handling to iterate through them.
events are the way that most programmers will expect this functionality to be accessed.
If you use a delegate, the consuming code can mess around with it in ways that you will want to prevent (such as resetting its invocation list). events do not allow that to happen.
As for your second question: Using an event you would create a class derived from EventArgs to hold the data, and pass that to the event when you raise it. The consumer will then have access to it.
See here for details: http://msdn.microsoft.com/en-us/library/system.eventargs.aspx