What is the syntax to declare an event in C#? - c#

In my class I want to declare an event that other classes can subscribe to. What is the correct way to declare the event?
This doesn't work:
public event CollectMapsReportingComplete;

You forgot to mention the type. For really simple events, EventHandler might be enough:
public event EventHandler CollectMapsReportingComplete;
Sometimes you will want to declare your own delegate type to be used for your events, allowing you to use a custom type for the EventArgs parameter (see Adam Robinson's comment):
public delegate void CollectEventHandler(object source, MapEventArgs args);
public class MapEventArgs : EventArgs
{
public IEnumerable<Map> Maps { get; set; }
}
You can also use the generic EventHandler type instead of declaring your own types:
public event EventHandler<MapEventArgs> CollectMapsReportingComplete;

You need to specify the delegate type the event:
public event Action CollectMapsReportingComplete;
Here I have used System.Action but you can use any delegate type you wish (even a custom delegate). An instance of the delegate type you specify will be used as the backing field for the event.

An Example
/// </summary>
/// Event triggered when a search is entered in any <see cref="SearchPanel"/>
/// </summary>
public event EventHandler<string> SearchEntered
{
add { searchevent += value; }
remove { searchevent -= value; }
}
private event EventHandler<string> searchevent;

public event EventHandler MyEvent;

public event [DelegateType] [EventName];

Related

How to pass an event through classes in C#

Here are 3 C# classes :
class StartClass
{
event StartEvent;
// some code
class MidClass
{
private StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
// some code
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
}
// some code
public void OnStartEvent ()
{
// code to be executed on StartEvent called
}
What's best way if you want to attach EndClass.OnStartEvent listener to StartClass.StartEvent event ?
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right ? How to attach StartClass.StartEvent to MidClass.StartEvent ?
In C# an event is much like a property. It is a wrapper for a delegate. Usually we are using auto-implemented events. But we can expand them. Properties have get and set accessors. Events have add and remove accessors.
class MidClass
{
private readonly StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
public event EventHandler StartEvent
{
add => _startClass.StartEvent += value;
remove => _startClass.StartEvent -= value;
}
}
Here, we create a StartEvent in MidClass that is a wrapper for the corresponding event in StartClass. StartClass.StartEvent must be public.
The advantage of this approach is that an event handler subscribing to MidClass.StartEvent will directly be attached to StartClass.StartEvent, with no intermediate call occurring when the event is risen.
One word to naming. Methods named OnEventName are usually used to raise events, while methods named PublisherName_EventName are used for event handlers.
class StartClass
{
public event EventHandler StartEvent;
private virtual void OnStartEvent()
{
StartEvent?Invoke(this, EventArgs.Empty);
}
}
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
_midClass.StartEvent += MidClass_StartEvent;
}
private void MidClass_StartEvent (object sender, EventArgs e)
{
// Code to be executed when StartEvent is triggered
}
}
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right?
If you want to keep the reference to StartClass a private implementation detail of MidClass: Yes, implementing a "proxy" StartEvent in MidClass is the right way to do it.
How to attach StartClass.StartEvent to MidClass.StartEvent ?
By attaching a listener to _startClass.StartEvent which just raises the corresponding event in MidClass:
class MidClass
{
public event EventHandler StartEvent;
private readonly StartClass _startClass;
public MidClass(StartClass startClass)
{
_startClass = startClass;
_startClass.StartEvent += (sender, e) => this.StartEvent?.Invoke(this, e);
}
...
}
Note that I also added the readonly modifier to _startClass: If the value of _startClass changes during the lifetime of MidClass, you need to detach your event handler from the old reference and attach it to the new reference.

What is the C# equivalent of 'Public Event TestEvent()'?

What is the C# equivalent of the following line of VB.NET code?
Public Event TestEvent()
Public Event TestEvent() creates an event without parameters in VB.Net.
Since you cannot simple use
public event void TestEvent;
in C#, you either have to create a new delegate
public delegate void TestEventEventHandler();
public event TestEventEventHandler TestEvent;
or simply use Action, which encapsulates a method without return value and without parameters.
public event Action TestEvent;
public event System.EventHandler testEvent;

C# Delegates and method signatures

From MSDN:
Any method that matches the delegate's
signature, which consists of the
return type and parameters, can be
assigned to the delegate.
So how is this possible:
public delegate void AlarmEventHandler(object sender, EventArgs e);
public event AlarmEventHandler Alarm;
protected virtual void OnAlarm(EventArgs e)
{
AlarmEventHandler handler = Alarm;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}
delegate AlarmEventHander and event AlarmEventHandler have different signatures yet handler can be assigned to Alarm.
Perhaps I'm mis-understanding delegates somewhat, and I would be very grateful if someone could explain where I'm going wrong.
A delegate is like a class. An event is like a property. When you declare an event in a class, you declare the type of event it is. In this case, AlarmEventHandler, which is an inner class of the top-level class this is a part of.
In the OnAlarm method, you get the instance of the AlarmEventHandler class that has been assigned to the event, and invoke it.
To clear things up, your code above is similar to this, using normal classes & references:
public class InnerClass {
public void MyMethod() { /* ... */ }
}
public InnerClass MyProperty { get; set; }
protected virtual void CallMyMethod() {
InnerClass cls = MyProperty;
if (cls != null)
cls.MyMethod();
}
Actually the signatures are the same. In .NET events are implemented with delegates.
public event AlarmEventHandler Alarm;
So above code is actually compiled by compiler as:
private AlarmEventHandler handler;
public event AlarmEventHandler Alarm
{
add { handler += value; }
remove { handler -= value; }
}
So event actually uses the same AlarmEventHandler delegate.
You are mixing delegates and events. While events are dependant on delegates, they are difference concepts completly. So should be taken separately.
Delegate is like type definition. Place where you use this delegate is like variable or property.
Event makes this delegate behave differently from the outside. Signature is still same.
An event is just a delegate. The delegate itselft can only be accessed from inside the class.
From the outside, only add and remove functionality for the event is possible to you can only do this:
myAlarm.Alarm+=new AlarmEventHandler(callPolice);
// or
myAlarm.Alarm-=new AlarmEventHandler(callPolice);
But from the inside of the class, Alarm is just a delegate of type AlarmEventHandler so you can do what your code shows:
AlarmEventHandler handler = Alarm;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
A function which takes a base-class as (non reference) parameter can be converted to a delegate that takes a derived class as parameter. This is because the function that takes base can be safely substituted where-ever a function that takes a derived class is used.
void TakesObject(object o)
{
...
}
Action<string> myAction=TakesObject;
You can call myAction only by passing in a string. And since every string is an object the contract for TakesObject is fulfilled.
In your case is works because every AlarmEventArgs is an EventArgs too. So the contract requirements of your eventhandler are less strict than the contract guarantees of the delegate type used by the event.
This is called co- and contra-variance. You have co-variance in return-types, and contra variance in parameters.
Check this article on MSDN:
Using Variance in Delegates (C# and Visual Basic)

Extension methods on an interface; delegates

I'm trying to get around dual inheritance in C# by re-implementing one of the parent classes as an interface with extension methods.
The problem I'm encountering is that
event EventHandler<EventArgs> Disconnecting;
public static void OnDisconnected(this AutoConnectClientBase target)
{
target.ClientConnectionState = ConnectionState.Disconnected;
target.Disconnected(target, EventArgs.Empty);
}
Can't be called in the extension methods.
I get: ***.Disconnecting can only appear on the left hand side of += or -=
While this makes perfect sense, in this case I wish it were not so.
What I'm looking for is a workaround that will permit me to access my interface's EventHandlers in the extension code. I'd like to keep it as simple as possible since the class I'm making into an interface is accessed by a large number of classes already and this is a change I'm reluctantly making in the first place.
Not sure what the problem is here -- the code you show doesn't match the error you cite (no call to .Disconnecting.
public interface IInterface
{
event EventHandler Event;
EventHandler Handler { get; set; }
}
public class Class : IInterface
{
public event EventHandler Event;
public EventHandler Handler { get; set; }
}
public static class InterfaceExtensions
{
public static void DoSomething(this IInterface i)
{
i.Event += (o, e) => Console.WriteLine("Event raised and handled");
i.Handler(i, new EventArgs());
}
}
I resolved this by doing the following:
Outside of the interface
public delegate EventHandler<EventArgs> MyEventHandler(object sender, EventArgs e);
Then inside the interface
event EventHandler<EventArgs> Disconnecting;
becomes
MyEventHandler Connected {get; set;}

Preventing same Event handler assignment multiple times

If I am assigning an event handler at runtime and it is in a spot that can be called multiple times, what is the recommended practice to prevent multiple assignments of the same handler to the same event.
object.Event += MyFunction
Adding this in a spot that will be called more than once will execute the handler 'n' times (of course).
I have resorted to removing any previous handler before trying to add via
object.Event -= MyFunction;
object.Event += MyFunction;
This works but seems off somehow. Any suggestions on proper handling ;) of this scenario.
Baget is right about using an explicitly implemented event (although there's a mixture there of explicit interface implementation and the full event syntax). You can probably get away with this:
private EventHandler foo;
public event EventHandler Foo
{
add
{
// First try to remove the handler, then re-add it
foo -= value;
foo += value;
}
remove
{
foo -= value;
}
}
That may have some odd edge cases if you ever add or remove multicast delegates, but that's unlikely. It also needs careful documentation as it's not the way that events normally work.
I tend to add an event handler in a path that's executed once, for example in a constructor.
You can implement your own storage of the delgates, and check for uniqueness when adding them to the event. See EventOwner2 class below for an example. I don't know how this is doing performance wise, but than again, that is not always an issue.
using System;
using System.Collections.Generic;
namespace EventExperiment
{
class Program
{
static void Main(string[] args)
{
IEventOwner e=new EventOwner2();
Subscriber s=new Subscriber(e);
e.RaiseSome();
Console.ReadKey();
}
}
/// <summary>
/// A consumer class, subscribing twice to the event in it's constructor.
/// </summary>
public class Subscriber
{
public Subscriber(IEventOwner eventOwner)
{
eventOwner.SomeEvent += eventOwner_SomeEvent;
eventOwner.SomeEvent += eventOwner_SomeEvent;
}
void eventOwner_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine(DateTimeOffset.Now);
}
}
/// <summary>
/// This interface is not essensial to this point. it is just added for conveniance.
/// </summary>
public interface IEventOwner
{
event EventHandler<EventArgs> SomeEvent;
void RaiseSome();
}
/// <summary>
/// A traditional event. This is raised for each subscription.
/// </summary>
public class EventOwner1 : IEventOwner
{
public event EventHandler<EventArgs> SomeEvent = delegate { };
public void RaiseSome()
{
SomeEvent(this,new EventArgs());
}
}
/// <summary>
/// A custom event. This is raised only once for each subscriber.
/// </summary>
public class EventOwner2 : IEventOwner
{
private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>();
public event EventHandler<EventArgs> SomeEvent
{
add
{
lock (handlers)
if (handlers!=null&&!handlers.Contains(value))
{
handlers.Add(value);
}
}
remove
{
handlers.Remove(value);
}
}
public void RaiseSome()
{
EventArgs args=new EventArgs();
lock(handlers)
foreach (EventHandler<EventArgs> handler in handlers)
{
handler(this,args);
}
}
}
}
What is the access modifier of 'object'?
If it's private, you only need to worry about the containing object setting the event handler.
If it's internal, you only need to worry about the containing assembly setting the event handler.
If it's public, then it's wide-open.
If 'object' can be made private on the containing class, you can make your checks much more efficient by controlling event handler assignment in the local class.
If 'internal' or 'public' and uniqueness is a requirement, go with a wrapper class that hides 'object' and instead exposes a method for assigning an event handler with your checks behind it to ensure uniqueness.

Categories