I am creating a Dictionary of events and I want to declare those events inside the initialization of that dictionary instead of declaring them somewhere else and placing the links to the dictionary.
static event EventDelegate Event1;
static event EventDelegate Event2;
static event EventDelegate Event3;
public enum EventTypes
{
Event1,
Event2,
Event3,
}
public static Dictionary<EventTypes, EventDelegate> events = new Dictionary<EventTypes, EventDelegate>
{
{EventTypes.Event1, Event1},
{EventTypes.Event2, Event2},
{EventTypes.Event3, Event3},
};
So I want to do something like that:
{EventTypes.Event1, new event EventDelegate Event1}
Is that possible?
How about wrapping the events?
class MyEventWrapper
{
public event EventDelegate Handlers;
public void Raise(object sender, EventArgs args)
{
Handlers?.Invoke(sender, args);
}
}
//
Dictionary<EventTypes, MyEventWrapper> eventMap = new Dictionary<EventTypes, MyEventWrapper>
{
{ EventTypes.Event1, new MyEventWrapper() },
{ EventTypes.Event2, new MyEventWrapper() },
};
//
eventMap[EventTypes.Event1].Handlers += (s, a) => { };
eventMap[EventTypes.Event2].Handlers += (s, a) => { };
//
eventMap[EventTypes.Event1].Raise(this, new EventArgs());
If you define your dictionary like this:
public delegate void EventDelegate(object data);
public static Dictionary<EventTypes, EventDelegate> Events =
new Dictionary<EventTypes, EventDelegate>
{
{ EventTypes.Event1, (EventDelegate)((_) => { }) },
{ EventTypes.Event2, (EventDelegate)((_) => { }) },
{ EventTypes.Event3, (EventDelegate)((_) => { }) },
};
public enum EventTypes
{
Event1,
Event2,
Event3,
}
Then this code works a treat:
Events[EventTypes.Event1] += (object data) => Console.WriteLine($"Event1 (1): {data}");
Events[EventTypes.Event1] += (object data) => Console.WriteLine($"Event1 (2): {data}");
Events[EventTypes.Event2] += (object data) => Console.WriteLine($"Event2: {data}");
Events[EventTypes.Event1]("A");
Events[EventTypes.Event2]("B");
Events[EventTypes.Event3]("C");
The output I get is:
Event1 (1): A
Event1 (2): A
Event2: B
You are then clearly declaring those events inside the initialization of the dictionary.
Related
Zero to many methods are added from on or more methods on classes
I pass a Guid as arg
Currently, I have subscribed the EventHandler as a wrapped action
When unsubscribing the Action I expect to still be able to fire the other delegates.
public class SomeHandler : IDisposable
{
private event EventHandler<Guid>? _onChangedSync;
public void AddOnChanged(Action<Guid> eventDelegate)
=> _onChangedSync += (_, arg) => eventDelegate(arg);
public void UnsubscribeOnChanged(Action<Guid> actionEvent)
{
var invocations = _onChangedSync?.GetInvocationList().Cast<EventHandler<Guid>>().ToList();
var found = invocations?.FirstOrDefault(del => del.Method == actionEvent.Method);
_onChangedSync -= found;
}
public void Change(Guid clinicId)
{
if (_onChangedSync != null)
{
_onChangedSync.Invoke(this, clinicId);
}
}
public void Dispose()
{
if (_onChangedSync != null)
{
foreach (var e in _onChangedSync.GetInvocationList().Cast<EventHandler<Guid>>())
{
_onChangedSync -= e;
}
}
GC.SuppressFinalize(this);
}
}
This is the unit test I use.
Subscribe 2 separate methods/actions
Unsubscribe 1 of the methods/actions
Expect only one of them to be called
Currently _callCount == 2 after run test
public class SomeTests
{
private int _callCount;
private void ListenAction1(Guid arg)
=> _callCount++;
private void ListenAction2(Guid arg)
=> _callCount++;
[Fact]
public void Unsubscribed_events_must_not_be_invoked()
{
// Given
var testArg = Guid.Parse("54b9bfde-ef2b-4aab-b705-4c59371c0e4f");
using var state = new SomeHandler();
state.AddOnChanged(ListenAction1);
state.AddOnChanged(ListenAction2);
state.UnsubscribeOnChanged(ListenAction1);
// When
state.Change(testArg);
// Then
Assert.Equal(expected: 1, _callCount);
}
}
How do I remove one of the subscribed from the EventHandler by passing the action again and comparing it with the rest of the invocations?
I'm trying to write a method that attaches adds itself to an event, and then removes itself after it was invoked.
Those are my failed attempts:
https://imgur.com/a/PvrPUYY
public Action myEvent = () => {};
void Bar(){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
Debug.Log("Attempt 0");
myEvent -= invokeThenUnsubscribe;
};
myEvent += invokeThenUnsubscribe;
SubscribeOnce(myEvent, () => Debug.Log("Attempt 1"));
myEvent.SubscribeOnce(() => Debug.Log("Attempt 2"));
myEvent.Invoke();
myEvent.Invoke();
}
public Action SubscribeOnce( Action a, Action actionToSubscribe ) {
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () =>
{
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
return a;
}
public static class ActionsEx {
public static void SubscribeOnce(this Action a, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
a -= invokeThenUnsubscribe;
};
a += invokeThenUnsubscribe;
}
}
I realize it's happening because, after using +=, I lose the reference to the new event.
Does anyone have any idea how I can achieve the effect I'm looking for?
Thanks in advance
When you call SubscribeOnce, you aren't passing a reference to "the event" (technically a field, but: semantics); instead, you're reading the current value of the event (field), and passing that to the method. At that point, it the parameter value is completely divorced from the event (field), and no change to a (now a captured parameter) will have any effect on the original event (field). You're only "unsubscribing" a local delegate that has nothing to do with the event (field).
To do what you'd want, you'd need to pass the originating object in as the parameter, i.e. something like:
public static void SubscribeOnce(this SomeType obj, Action actionToSubscribe){
Action invokeThenUnsubscribe = null;
invokeThenUnsubscribe = () => {
actionToSubscribe.Invoke();
obj.TheEvent -= invokeThenUnsubscribe;
};
obj.TheEvent += invokeThenUnsubscribe;
}
}
where SomeType (which could be an interface) defines an event Action TheEvent;, with usage this.SubscribeOnce(() => Debug.Log("Attempt 2")); (or whatever object you want to use)
I am not crazy about the fact that you are mixing actions with events. Instead, I would use a more object oriented approach and separate the two concepts by creating an interface to represent the subscribable objects that expose some event and then use an extension method to allow all actions to subscribe once to these objects:
//Delegate to handle events
public delegate void SomeEventEventHandler(ISubscribable sender, EventArgs args);
//Interface that describes an object you can subscribe to
public interface ISubscribable
{
event SomeEventEventHandler MyEvent;
}
//Implementation of a subscribable object (base class)
public class SomeObject : ISubscribable
{
private SomeEventEventHandler _handler;
public event SomeEventEventHandler MyEvent
{
add { _handler += value; }
remove { _handler -= value; }
}
public void RaiseEvent()
{
_handler?.Invoke(this, new EventArgs());
}
}
//Extension method for actions
public static class ActionExtensions
{
public static void SubscribeOnce(this Action action, ISubscribable subscribable)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
subscribable.MyEvent += handler;
}
}
//Usage:
var obj = new SomeObject();
obj.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
Action invokeThenUnsubscribe = () => { Console.WriteLine("Attempt 1"); };
invokeThenUnsubscribe.SubscribeOnce(obj);
Action invokeThenUnsubscribe2 = () => { Console.WriteLine("Attempt 2"); };
invokeThenUnsubscribe2.SubscribeOnce(obj);
obj.RaiseEvent();
obj.RaiseEvent();
obj.RaiseEvent();
Output:
Attempt 0 (always fires).
Attempt 1
Attempt 2
Attempt 0 (always fires).
Attempt 0 (always fires).
Alternatively, you can skip the action extension all together and provide this functionality as a method of the class:
//In class SomeObject
public void SubscribeOnce(Action action)
{
SomeEventEventHandler handler = null;
handler = new SomeEventEventHandler((ISubscribable subscribable, EventArgs args) =>
{
action();
subscribable.MyEvent -= handler;
});
this.MyEvent += handler;
}
//Usage:
var obj2 = new SomeObject();
obj2.MyEvent += (ISubscribable sender, EventArgs args) =>
{
Console.WriteLine("Attempt 0 (always fires).");
};
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 1"); });
obj2.SubscribeOnce(() => { Console.WriteLine("Attempt 2"); });
obj2.RaiseEvent();
obj2.RaiseEvent();
obj2.RaiseEvent();
This produces the same output.
I have the following code, which based on a condition assigns or unassigns a parameterized event handler to some objects, using an anonymous (lambda) method:
{
if (condition)
foreach (var channel in dataSource.Channels)
{
channel.NewSamples += (s, vals) => AddSamples(channel.Index, vals);
}
}
else
{
foreach (var channel in dataSource.Channels)
{
channel.NewSamples -= (s, vals) => AddSamples(channel.Index, vals);
}
}
}
private void AddSamples(int channelIndex, IEnumerable<int> samples)
{
/// do work
}
I suspect this wouldn't unsubscribe correctly, and so I would like to pass channel.Index as parameter to a named handler, but I don't know how to do it.
You can store your handlers in a dictionary, by channel, like this:
Dictionary<Channel, YourEventHandler> _handlers = new Dictionary<Channel, YourEventHandler>();
...
if (condition)
foreach (var channel in dataSource.Channels)
{
if (!_handlers.ContainsKey(channel)) {
YourEventHandler handler = (s, vals) => AddSamples(channel.Index, vals);
channel.NewSamples += handler;
_handlers[channel] = handler;
}
}
}
else
{
foreach (var channel in dataSource.Channels)
{
if (_handlers.ContainsKey(channel)) {
channel.NewSamples -= _handlers[channel];
_handlers.Remove(channel);
}
}
}
Hi guys I have a ExternalDataService that is constantly firing real time data, it is contained in a lib.
I have a wrapper class that subscribes and puts the updates on a Observable..
public class MyService
{
private ExternalDataService _externalDataService;
public MyService()
{
_externalDataService= new ExternalDataService ();
}
public IObservable<double> GetData()
{
return Observable.Create<double>(i =>
{
_externalPriceService.OnDataChanged += (s, e) => { i.OnNext(e); };
return () =>
{
// what do I do here?
};
});
}
}
Consumed as...
var p = new MyService();
var disposable = p.GetData().Subscribe(i => Console.WriteLine(i));
How would I unsubscribe from _externalPriceService.OnDataChanged when the Dispose is called on disposable?
Use Observable.FromEvent or Observable.FromEventPattern, instead of Observable.Create. Then you just Dispose the subscription.
Depending on your EventHandler definition, it would be something like this:
public class ExternalDataService {
public EventHandler<DataChangedEventArgs> OnDataChanged;
}
public class DataChangedEventArgs : EventArgs {
public Double Data {
get; set;
}
}
public class MyService {
private ExternalDataService _externalDataService;
public MyService()
{
_externalDataService= new ExternalDataService ();
}
public IObservable<double> GetData()
{
return Observable.FromEventPattern<DataChangedEventArgs>(eh => _externalDataService.OnDataChanged += eh, eh => _externalDataService.OnDataChanged -= eh)
.Select(e => e.EventArgs.Data);
}
}
You can also do something like this:
public IObservable<double> GetData()
{
Action<object, double> dataHandler = null;
return Observable.Create<double>(i =>
{
dataHandler = (s, e) => { i.OnNext(e); };;
_externalDataService.OnDataChanged += dataHandler;
return Disposable.Create(() =>
{
_externalDataService.OnDataChanged -= dataHandler;
});
});
}
edit: stupid typos
I have a class with a method in which a string will be passed. That method will do some things to that string and it then passes the string to a certain object which can do other things with the string.
So it basically looks like this:
class Main
{
public Main()
{
strClass str = new strClass(this);
}
public function handler ( )
{
console.log("No string is passed yet, but this method is called from receiveData()");
}
}
class strClass
{
object handler;
public strClass ( handler )
{
// save the object
this.handler = handler;
}
public receiveData ( string str )
{
// This method does some stuff with the string
// And it then passes it on to the supplied object (handler) which will do
// the rest of the processing
// I'm calling the "handler" method in the object which got passed in the
// constructor
Type thisType = this.handler.GetType();
MethodInfo theMethod = thisType.GetMethod("handler");
theMethod.Invoke(this.handler, null);
}
}
Now this code works good, with the reflection stuff. But i was wondering, shouldn't this be possible (and maybe even better?) with delegates?? If so, how can i implement this by using a delegate instead?
Couldn't you use interfaces instead:
interface IStringHandler {
void HandleString(string s);
}
class strClass
{
IStringHandler handler = null;
public strClass(IStringHandler handler)
{
this.handler = handler;
}
public void ReceiveData(string s)
{
handler.HandleString(s);
}
}
class Main : IStringHandler
{
// Your code
}
A delegate is a better option here.
class Main
{
public Main()
{
StrClass str = new StrClass(this.Handler);
}
public void Handler ( )
{
//called from recieve data
}
}
class StrClass
{
readonly Action _handler;
public StrClass ( Action callback)
{
// save the object
this._handler = callback;
}
public void receiveData( string str )
{
this._handler();
}
}
You can do it with an Action like this:
class Main
{
public Main()
{
strClass str = new strClass(newString =>
{
console.log("This string I got back: " + newString);
});
}
}
class strClass
{
Action<string> callback;
public strClass (Action<string> callback)
{
// save the action
this.callback = callback;
}
public receiveData ( string str )
{
// Do something with the string
callback(str);
}
}
Even nicer than using delegates whould be using the
Chain of Responsibility design pattern, which does exactly what you need :).
Firstly, if you must call an unknown method by name, use dynamic - it is heavily optimised for this (although still not a great idea):
((dynamic)handler).handler(); // but please don't use this! see below
However, I would instead look at either an Action<string> (or maybe Func<string,string>), or an interface with a known method on it.
Basically, you want to change how your StrClass object react to data begin received. Sounds like events to me.
something like this, where you have handling methods both in the Main and in a generic HandlerObject:
class StrClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = null;
public void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
private string receivedString;
public string ReceivedString
{
get;
set
{
string oldStr = receivedString;
receivedString = value;
PropertyChanged(receivedString, new PropertyChangedEventArgs("ReceivedString"));
}
}
public void receiveData(string str)
{
//event fires here
ReceivedString = str;
}
}
class HandlerObject
{
public void HandlerMethod1(string s)
{
//magic
}
public void HandlerMethod2(string s)
{
//different kind of magic
}
}
class Program
{
static void HandlerMethod3(string s)
{
//another kind of magic!
}
static void Main(string[] args)
{
StrClass class1 = new StrClass();
StrClass class2 = new StrClass();
StrClass class3 = new StrClass();
HandlerObject handler = new HandlerObject();
class1.PropertyChanged += (s, e) => { handler.HandlerMethod1(s.ToString()); };
class2.PropertyChanged += (s, e) => { handler.HandlerMethod2(s.ToString()); };
class3.PropertyChanged += (s, e) => { HandlerMethod3(s.ToString()); };
}
}