I know we shouldn´t care if an eventhandler was registered for an event from outside that event, as seen here for instance How to determine if an event is already subscribed
Thus I have some code within the events-accessor to prevent any users of my API bothering about if or if not they already registered the handler:
private EventHandler foo;
public event EventHandler Foo
{
add
{
if (foo == null || !foo.GetInvocationList().Contains(value))
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Now I want to write a test to verify that this check works correct:
[Test]
public void MyTest()
{
myInstance.Foo += DoSomething;
myInstance.Foo += DoSomething;
}
void DoSomething(object sender, EventArgs args) { ... }
But how do I check if foo contains only a single handler, not two? Should I use reflection on foo to get its invocation-list or is there any better way?
Have DoSomething count the number of times its invoked. Raise the Foo event, and assert that DoSomething was executed only once.
[Test]
public void MyTest()
{
int invokeCount = 0;
EventHandler handler = (object sender, EventArgs args) => invokeCount++;
myInstance.Foo += handler;
myInstance.Foo += handler;
myInstance.SomeMethodThatRaisesFoo();
Assert.AreEqual(1, invokeCount);
}
Related
I want pass event to another event,now I use function to do that.
Can C# pass event like b.WriteEvent += a.WriteEvent ?
If I had a lot class,and just want pass argument to above class.
I want write like : a.event += b.event. b.event += c.event
Instead of a lot no use method.
Thanks.
class Program
{
static void Main(string[] args)
{
ClassA a = new ClassA();
ClassB b = new ClassB();
a.WriteEvent += MainWrite;
b.WriteEvent += a.WireFunction; // Now I use
//b.WriteEvent += a.WriteEvent; <= Can I use like this ?
b.WireFunction("some str");
Console.ReadLine();
}
static void MainWrite(string str)
{
Console.WriteLine(str);
}
}
class ClassA
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
class ClassB
{
public event Handler WriteEvent;
public void WireFunction(string str)
{
WriteEvent(str);
}
}
public delegate void Handler(string str);
Fact:You cannot provide an event that subscribes to an event.
All delegates (events, actions or funcs) ar multicast delegates in C#.
That means you can subscribe to an event multiple times.
In order to subscribe to an event you have to provide an action or a function. (I use the term function instead of method because we may provide a lambda)
What follows is snipped that subscribes all subscribers of Event1 to Event2.
I believe this is what you intend to do.
public class SomeClass
{
public event EventHandler Event1;
public event EventHandler Event2;
public SomeClass()
{
Event1 += Subscriber1;
Event1 += Subscriber2;
var subscribers = Event1.GetInvocationList();
if(subscribers != null)
{
foreach(var subscriber in subscribers)
{
EventHandler realSubscriber = (EventHandler)subscriber;
Event2 += realSubscriber;
}
}
Event1(this, EventArgs.Empty);
Event2(this, EventArgs.Empty);
}
public void Subscriber1(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 1 invoked");
}
public void Subscriber2(object sender, EventArgs e)
{
Console.WriteLine("Subscriber 2 invoked");
}
}
Creating an instance of the SomeClass will print:
Subscriber 1 invoked
Subscriber 2 invoked
Subscriber 1 invoked
Subscriber 2 invoked
EDIT:
I tried to move the logic to an extension method and also to a normal utility method. Both did not work very well because events are null when they have no subscribers. Passing an event without subscribers would then result in the same behaviour as if null was passed. For now, this is the best I could come up with.
How should I fix SonarLint Rule S1172 "Unused method parameters should be removed" when I create EventHandler methods.
public void Subscribe()
{
MyEvent += OnMyEvent;
}
public void UnSubscribe()
{
MyEvent -= OnMyEvent;
}
private void OnMyEvent(object sender, EventArgs e)
{
DoSomething();
}
You could rewrite the code with Reactive Extensions and making 'Observables' but that is quite complex solution for simple event handlers. Another option could be to rewrite the code like:
public void Subscribe()
{
MyEvent += (s,e) => DoSomething();
}
But the question then is how do you do the UnSubscribe()? By my opinion the unused parameters is not applicable to event handler methods. But it might be difficult to make detection for that in SonarLint.
If you need to unsubscribe, you'll need to store the delegate (remove static for proper code, this is pasted from a hacked console app project):
public static event EventHandler TestEvent;
private static EventHandler saved = (s, e) => DoSomething();
static void Main(string[] args)
{
TestEvent += saved;
TestEvent -= saved;
}
internal static void DoSomething()
{
}
Or use a mass-unsubscribe:
foreach (Delegate d in TestEvent.GetInvocationList())
{
TestEvent -= (EventHandler)d;
}
Or if you own the event, you could also use this to unsubscribe all:
TestEvent = null;
Or just use the syntax you've always used and create a non-anonymous method, like you show above. There's nothing wrong with that syntax. You could do the obligatory
if (sender == null)
throw ArgumentNullException(nameof(sender));
to get rid of the warning ;)
I have this class that contain several events that from my main i register and update my UI:
public class MyClass
{
public delegate void event1Delegate();
public event event1Delegate event1Handler;
public delegate void event2Delegate();
public event event2Delegate event2Handler;
public delegate void FinishWorkDelegate();
public event FinishWorkDelegate FinishWorkEventHandler;
public void DoWork()
{
// bla bla
if (FinishWorkEventHandler != null)
FinishWorkEventHandler();
}
}
And from my main UI register to this events inside my button clock event:
private void radMenuItemSimultaneouslyPlay_Click(object sender, EventArgs e)
{
MyClass obj = new MyClass();
job.event1Handler += job_event1Handler;
job.event2Handler += job_event2Handler;
job.FinishWorkEventHandler += job_FinishWorkEventHandler;
job.doWork();
}
Now when FinishWorkEventHandler fired this means that my operation done:
private void job_OnFinishJobThreadEvent()
{
labelStatus.Text= "Finished!";
}
And here i want to unsubscride to MyClass events so i wonder if it's OK to change this event from FinishWorkDelegate() into FinishWorkDelegate(MyClass obj) and that from here i have access to my object and in this case i can unsubscride to my events.
Is it OK to do that ?
Is it OK to do that ?
Yeah why not ? in fact there is already a built-in delegate for this called EventHandler<T>, instead of creating a new delegate you can use that:
public event EventHandler<EventArgs> FinishWorkEventHandler;
public void DoWork()
{
// bla bla
if (FinishWorkEventHandler != null)
FinishWorkEventHandler(this, EventArgs.Empty);
}
Here you can replace this with your current instance (if you want to trigger it from outside of the class ofcourse) and then pass it to event handler.in the event handler you can access your object like this:
private void OnFinish(object sender, EventArgs e)
{
var myObject = sender as MyClass;
if (myObject != null)
{
myObject.event1Handler -= job_event1Handler;
myObject.event2Handler -= job_event2Handler;
}
}
I have two classes, and i have to make an event to communicate between these classes.
Class a
{
public delegate void delegat(int a);
public event delegat exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
// My event named exit should run here, but I get exception!
exit(100);
}
}
Class b
{
a instance=new a();
a.exit+=new a.delegat(my_fun);
...
private void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
But the thing is that i get exception: "object reference not set to an instance of an object".
I can't understand what Am I doing wrong? Where should I make a new instance of this?
Thanks for help!
You are trying to assign the exit event on the class itself and not the instance e.g.
a.exit += ...
Should be:
instance.exit += ...
You also aren't checking whether your exit event has been assigned before attempting to fire it. There are other issues which you haven't taken into consideration like race conditions.
Here is a general example of a relatively safe way of handling events e.g.
public class A
{
public delegate void ExitHandler(object sender, int a);
public event ExitHandler Exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
OnExit(100);
}
protected virtual void OnExit(int a)
{
// take a reference to the event (incase it changes)
var handler = Exit;
if (handler != null)
{
handler(this, a);
}
}
}
public class B
{
private A _a;
public B()
{
_a = new A();
_a.Exit += (sender, value) => my_fun(value);
}
private void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
I would change "class a" code for calling the event to as follows:
Class a
{
public delegate void delegat(int a);
public event delegat exit;
...
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
if (this.exit != null) // just in case a_FormClosed fires before assigning the event
exit(100);//here should run my event named exit but i get exception!
}
}
Verify if there is any subscriber exist to your event before raising it:
if (exit != null)
exit(100);
Another option - subscribing dummy event handler when you are defining event in class A:
public event delegat exit = (_) => { };
Also use PascalCase naming for types, events and methods. And there is predefined delegate in .NET which receives one argument and returns void: Action<T>
Try this
namespace foo
{
public delegate void delegat(int a);
Class a
{
public event delegat exit;
private void a_FormClosed(object sender, FormClosedEventArgs e)
{
if(exit != null)
{
exit(100);//here should run my event named exit but i get exception!
}
}
}
}
Class b
{
a instance=new a();
instance.exit+=new delegat(my_fun);
...
priavte void my_fun(int x)
{
if(x==100)
do_smth;
...
}
}
I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.