I have an industrial computer with some Digital I/O pins. The manufacturer provides some C++ libraries and examples to handle pin status change.
I need to integrate this events onto a C# application. AFAIK the most simple way to perform this is:
Make a managed C++/CLI wrapper for the manufacturer libraries that fires events when interruptions are issued from the DIO pins.
Reference that wrapper and handle the events in the C# part as it they were normal C# events.
I have tried to make this work with some mock objects with no luck. From the docs, the function EventHandler should do most of the "dirty work" in my case. Following info available in old threads and the EventHandler example in the MSDN docs I ended up with this test code:
C++/CLI
using namespace System;
public ref class ThresholdReachedEventArgs : public EventArgs
{
public:
property int Threshold;
property DateTime TimeReached;
};
public ref class CppCounter
{
private:
int threshold;
int total;
public:
CppCounter() {};
CppCounter(int passedThreshold)
{
threshold = passedThreshold;
}
void Add(int x)
{
total += x;
if (total >= threshold) {
ThresholdReachedEventArgs^ args = gcnew ThresholdReachedEventArgs();
args->Threshold = threshold;
args->TimeReached = DateTime::Now;
OnThresholdReached(args);
}
}
event EventHandler<ThresholdReachedEventArgs^>^ ThresholdReached;
protected:
virtual void OnThresholdReached(ThresholdReachedEventArgs^ e)
{
ThresholdReached(this, e);
}
};
public ref class SampleHandler
{
public:
static void c_ThresholdReached(Object^ sender, ThresholdReachedEventArgs^ e)
{
Console::WriteLine("The threshold of {0} was reached at {1}.",
e->Threshold, e->TimeReached);
Environment::Exit(0);
}
};
void main()
{
return;
CppCounter^ c = gcnew CppCounter(20);
c->ThresholdReached += gcnew EventHandler<ThresholdReachedEventArgs^>(SampleHandler::c_ThresholdReached);
Console::WriteLine("press 'a' key to increase total");
while (Console::ReadKey(true).KeyChar == 'a') {
Console::WriteLine("adding one");
c->Add(1);
}
}
C#
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CppCounter cc = new CppCounter(5);
//cc.ThresholdReached += cs_ThresholdReached; //<--This is the offending line
Console.WriteLine("press 'a' key to increase total");
while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
cc.Add(1);
}
}
static void cs_ThresholdReached(object sender, ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
Environment.Exit(0);
}
}
class Counter
{
private int threshold;
private int total;
public Counter(int passedThreshold)
{
threshold = passedThreshold;
}
public void Add(int x)
{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}
public class ThresholdReachedEventArgs : EventArgs
{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
}
What am I doing wrong? Is it something I am missing?
public class ThresholdReachedEventArgs : EventArgs
The code is correct, except for this minor glitch. You accidentally re-declared this class in your C# code. Now there are two, one from your C++/CLI project and another from your C# project. That is a problem, type identity in .NET is not just determined by the namespace name and class name, it also includes the assembly it came from.
So these are two distinct types, the compiler tries to tell you that the C# version of it is not the correct one. That they have the same name doesn't exactly help you decode the error message :)
Very easy to fix, simply delete the class declaration from your C# code. Now the compiler will use the C++/CLI version of it.
Related
I'm trying to dynamically load a class, from a dll, at run time then cast it as in interface.
I have a class define in a dll called MyClass.dll like below.
namespace MyClass
{
public class Class1 : IMyInterface
{
public int MyNumber { get; set; } = 6;
public int GetMyNumber()
{
return MyNumber;
}
}
}
Then, i have an interface defined in a shared dll like below:
namespace Common
{
public interface IMyInterface
{
int MyNumber { get; set; }
int GetMyNumber();
}
}
Finally, i have my code attempted to load the assembly and assign it as a IMyInterface.
private void Form1_Load(object sender, EventArgs e)
{
string fName = #"D:\Development\Research\Dynamic Load\MyClass\bin\Debug\MyClass.dll";
Assembly decoupledAssembly = Assembly.LoadFrom(fName);
if (decoupledAssembly != null) //All Good
{
Type t = decoupledAssembly.GetType("MyClass.Class1");
//Good here too, it finds it just fine.
IMyInterface mi = (Activator.CreateInstance(t) as IMyInterface);
//Now i'm screwed screwed.
if (mi != null)
MessageBox.Show(mi.GetMyNumber().ToString());
}
}
You can see in the comments where it is breaking. I find the type, no problem, but returns null when I attempted to cast it as my interface.
Thanks in advance for your help.
You have to reference the full path to the interface. Strange, but once I did this it started working. See the changes below.
namespace MyClass
{
public class Class1 : Common.IMyInterface
{
public int MyNumber { get; set; } = 6;
public int GetMyNumber()
{
return MyNumber;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
string fName = #".....\MyClass.dll";
Assembly decoupledAssembly = Assembly.LoadFrom(fName);
if (decoupledAssembly != null) //All Good
{
Type t = decoupledAssembly.GetType("MyClass.Class1");
//Good here too, it finds it just fine.
Common.IMyInterface mi = (Activator.CreateInstance(t) as Common.IMyInterface);
//Now it works.
if (mi != null)
MessageBox.Show(mi.GetMyNumber().ToString());
}
}
I want to get my field >>> NumberOfElementsInMyList from sender, How Can I do it? I couldn't find this kind of question here (what I have seen, was about windows forms), so ...
class Program
{
static void Main(string[] args)
{
Publisher PublisherObject = new Publisher();
PublisherObject.NumberAdded += PublisherObject_NumberAdded;
PublisherObject.AddNumber(int.Parse(Console.ReadLine());
}
static void PublisherObject_NumberAdded(object sender, EventArgs e)
{
//I want to write on the console "NumberOfElementsInMylist"
//I tried:
//sender.NumberOfElementsInMylist -- not works
//Publisher obj=(publisher)sender and then sender.NumberOfElementsInMylist
//not works
Console.WriteLine("number of elements in list is ---> "+ ???? );
}
}
class Publisher
{
public event EventHandler NumberAdded;
public int NumberOfElementsInMyList;
List<int> MyList=new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAdded(this, new EventArgs());
}
}
To literally answer your question, the reason why you can't access the NumberOfElementsInMyList field is because when you create the EventArgs, your instance of Publisher is being cast as an object (which you can do since all classes inherit from object.) So to see the properties (or field) of Publisher, you have to cast the sender as Publisher.
var numberOfElements = ((Publisher)sender).NumberOfElementsInMyList;
A downside to this is that hypothetically, sender might not be a Publisher. Because sender is an object, it could technically be anything.
You can also create your own event handler delegate and event args instead of using the boilerplate EventHandler delegate.
public delegate void NumberAdded(Publisher source, NumberAddedEventArgs eventArgs);
public class NumberAddedEventArgs : EventArgs
{
public NumberAddedEventArgs(int numberAdded, numberOfItemsInList)
{
NumberAdded = numberAdded;
NumberOfItemsInList = numberOfItemsInList;
}
public int NumberAdded { get; private set; }
public int NumberOfItemsInList { get; private set; }
}
public class Publisher
{
public event EventHandler NumberAddedEvent;
public int NumberOfElementsInMyList;
List<int> MyList = new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAddedEvent?.Invoke(this, new NumberAddedEventArgs(NumberToAdd,
NumberOfElementsInMyList));
}
}
var numberOfElementsInList = args.NumberOfItemsInList; // much better!
The (object sender, EventArgs args) is a strange convention. In any other scenario we would create strongly-typed methods and delegates. But in this case there's a tendency to use something that's not strongly-typed because it's a convention.
You can simply cast the sender, can't you?
var publisher = (Publisher)sender;
Console.WriteLine(publisher.NumberOfElementsInMyList);
Or more safely:
Console.WriteLine(
(sender as Publisher)?.NumberOfElementsInMyList?.ToString() ?? "sender is not a publisher!");
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 have a form that has a button to get a method executed in another class.
Code on the form:
public delegate void CustomPreviewCreate();
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
CustomPreviewCreate_Do();
}
}
This event then gets handled in another class. What I would like to achieve is that I can feed back to the form some form of return value if the method correctly executed.
What I tried so far does not get me the result.
Here is the code:
public void Initialize()
{
SubAsstViewPartControl.CustomPreviewCreate_Do += SubAsstViewPartControl_CustomPreviewCreate_Do;
// this gives me a the compiler error that the return type is wrong
}
private bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return false;
}
Is there any direct way to return value from an event handler or I need to use a separate static field to store the event result in?
Update:
Per #Jon's comment, which seemed the simplest to me, I added an answer below demonstrating the simplest approach.
The common approach is to encapsulate your value in the type of EventArgs your event expects. For example, the Framework's CancelEventArgs contains a settable bool Cancel property, allowing each CancelEventHandler to assign a value. The sender can then read the property after the event has been invoked. You could also use a container-like EventArgs class if you want to collect separate values from individual event handlers. For example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class SingleValueEventArgs : EventArgs
{
public int Value { get; set; }
}
public class MultiValueEventArgs : EventArgs
{
private List<int> _values = new List<int>(); // Private to prevent handlers from messing with each others' values
public IEnumerable<int> Values
{
get { return _values; }
}
public void AddValue(int value) { _values.Add(value); }
}
public class Exposer
{
public event EventHandler<SingleValueEventArgs> WantSingleValue;
public event EventHandler<MultiValueEventArgs> WantMultipleValues;
public void Run()
{
if (WantSingleValue != null)
{
var args = new SingleValueEventArgs();
WantSingleValue(this, args);
Console.WriteLine("Last handler produced " + args.Value.ToString());
}
if (WantMultipleValues != null)
{
var args = new MultiValueEventArgs();
WantMultipleValues(this, args);
foreach (var value in args.Values)
{
Console.WriteLine("A handler produced " + value.ToString());
}
}
}
}
public class Handler
{
private int _value;
public Handler(Exposer exposer, int value)
{
_value = value;
exposer.WantSingleValue += exposer_WantSingleValue;
exposer.WantMultipleValues += exposer_WantMultipleValues;
}
void exposer_WantSingleValue(object sender, SingleValueEventArgs e)
{
Console.WriteLine("Handler assigning " + _value.ToString());
e.Value = _value;
}
void exposer_WantMultipleValues(object sender, MultiValueEventArgs e)
{
Console.WriteLine("Handler adding " + _value.ToString());
e.AddValue(_value);
}
}
class Program
{
static void Main(string[] args)
{
var exposer = new Exposer();
for (var i = 0; i < 5; i++)
{
new Handler(exposer, i);
}
exposer.Run();
}
}
}
Per Jon Skeet's comment, which seemed the simplest to me, the simplest approach seems to be as follows:
public delegate bool CustomPreviewCreate(); // here we declare a return type
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
bool returnval = CustomPreviewCreate_Do();
}
}
And then:
// the method is declared to return the same type
bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return true; // return the value of the type declared
}
I have a short events example from .NET 2.0 that I've been using as a reference point for a while. We're now upgrading to 3.5, though, and I'm not clear on the most idiomatic way to do things. How would this simple events example get updated to reflect idioms that are now available in .NET 3.5?
// Args class.
public class TickArgs : EventArgs {
private DateTime TimeNow;
public DateTime Time {
set { TimeNow = value; }
get { return this.TimeNow; }
}
}
// Producer class that generates events.
public class Metronome {
public event TickHandler Tick;
public delegate void TickHandler(Metronome m, TickArgs e);
public void Start() {
while (true) {
System.Threading.Thread.Sleep(3000);
if (Tick != null) {
TickArgs t = new TickArgs();
t.Time = DateTime.Now;
Tick(this, t);
}
}
}
}
// Consumer class that listens for events.
public class Listener {
public void Subscribe(Metronome m) {
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void HeardIt(Metronome m, TickArgs e) {
System.Console.WriteLine("HEARD IT AT {0}",e.Time);
}
}
// Example.
public class Test {
static void Main() {
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
// Args class.
public class TickArgs : EventArgs {
private DateTime TimeNow;
// Auto property used
public DateTime Time { get; set; }
}
// Producer class that generates events.
public class Metronome {
public event TickHandler Tick;
public delegate void TickHandler(Metronome m, TickArgs e);
public void Start() {
while (true) {
System.Threading.Thread.Sleep(3000);
// Thread safety introduced
TickHandler ticker = Tick;
if (ticker != null) {
// Object initialiser added
TickArgs t = new TickArgs {
Time = DateTime.Now;
}
ticker(this, t);
}
}
}
}
// Consumer class that listens for events.
public class Listener {
public void Subscribe(Metronome m) {
// Event handler replaced with llambda function
m.Tick += (mm, e) => System.Console.WriteLine("HEARD IT AT {0}",e.Time)
}
}
// Example.
public class Test {
static void Main() {
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
You can improve the Tick event like this
// Producer class that generates events.
public class Metronome {
// Add a dummy event handler and ensure that there's no unsafe thread issues
public event TickHandler Tick = (m, e) => {};
public delegate void TickHandler(Metronome m, TickArgs e);
public void Start() {
while (true) {
System.Threading.Thread.Sleep(3000);
// no need to check for null before calling
Tick(this, new TickArgs { Time = DateTime.Now; });
}
}
}
I don't have a compile to hand but you can improve the Tick event like this I think
// Producer class that generates events.
public class Metronome {
// Add a dummy event handler and ensure that there's no unsafe thread issues
public event EventHandler<TickArgs> Tick = (m, e) => {};
public void Start() {
while (true) {
System.Threading.Thread.Sleep(3000);
// no need to check for null before calling
Tick(this, new TickArgs { Time = DateTime.Now; });
}
}
}
For a start, you shouldn't define your own delegate types for events. Instead, use System.EventHandler<T> or System.EventHandler.
Also, you can use auto-implemented properties for your TickArgs class (which, incidentally, should really be called TickEventArgs, in accord with .NET conventions):
public class TickEventArgs : EventArgs
{
public DateTime Time
{
get;
set;
}
}
As for the events themselves, there are some gotchas that you should know about in .NET which you can read about in some of John Skeet's multithreading articles:
http://www.yoda.arachsys.com/csharp/events.html
Note that events in .NET 4 work differently and a lot of the gotchas that exist in 3.5 have been cleaned up.
For starters you can use auto generated properties:
public class TickArgs : EventArgs {
public DateTime Time {
set;
get;
}
}
You also do not need to instantiate the listening delegate:
public void Subscribe(Metronome m) {
m.Tick += HeardIt;
}
Here's how I would do it. First, note that I've made TickArgs sealed and immutable via readonly on the member variables. Second, I removed the TickHandler delegate and replaced with an EventHandler<TickArgs>. Thirdly, the Tick event itself is now private (renamed to _Tick) and accessed via a property. Lastly, the hook into the event in Listener uses a lambda expression (i.e. inline delegate) rather than an explicit method.
namespace Events3
{
using System;
// Args class.
public sealed class TickArgs : EventArgs
{
private readonly DateTime TimeNow;
public DateTime Time
{
get { return this.TimeNow; }
}
public TickArgs(DateTime TimeNow)
{
this.TimeNow = TimeNow;
}
}
// Producer class that generates events.
public sealed class Metronome
{
private event EventHandler<TickArgs> _Tick;
public event EventHandler<TickArgs> Tick
{
add { this._Tick += value; }
remove { this._Tick -= value; }
}
public void Start()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
EventHandler<TickArgs> tick = this._Tick;
if (tick != null)
{
tick(this, new TickArgs(DateTime.Now));
}
}
}
}
// Consumer class that listens for events.
public sealed class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += (sender, e) =>
{
System.Console.WriteLine("HEARD IT AT {0}", e.Time);
};
}
}
// Example.
public static class Test
{
static void Main()
{
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
}