I'm using dot net standard 2.1 and c# 8, I want to create an event for my class (interface), I follow this tutorial and I wrote an interface:
using System;
using Crawler.Paging;
namespace Crawler
{
public interface ICrawler
{
public event EventHandler NextPage;
protected virtual void OnNextPage(EventArgs e)
{
EventHandler handler = NextPage;
handler?.Invoke(this,e);
}
void Paging(IPaging paging);
}
}
but take me an error:
Error The event 'ICrawler.NextPage' can only appear on the left hand side of += or -=
I proceeded with this training, so where is the problem?
A simple event definition ending with ;
in a class is made of 2 parts, which are the event, which only contains both add/remove accessors (methods), and the handler delegate.
for
class Foo
{
public event EventHandler Bar;
}
equals to
class Foo
{
//The event
public event EventHandler Bar
{
add => _bar += value;
remove => _bar -= value;
}
//The handler value
private EventHandler _bar;
}
Note that the backing field is always private
, regardless of the access modifier of the event definition. So Bar?.Invoke()
is actually accessing the handler delegate directly, not the accessors, and can only be done within the class itself.
But a simple event definition ending with ;
in an interface is only the abstract event, which only contains both add/remove abstract accessors (abstract methods).
for
interface IFoo
{
event EventHandler Bar;
}
equals to
interface IFoo
{
public abstract EventHandler Bar;
//The following syntax is invalid but shows how it works.
/*
event EventHandler Bar
{
abstract add;
abstract remove;
}
*/
}
The default interface implementation feature in C# does not breaking-change it, for an interface cannot contain any field (which defines what interface in C# is). As long as the handler delegate does not exist, it is not possible to access it directly, therefore Bar?.Invoke()
is invalid.
There is a workaround by using a manually implemented event (which is also a default implementation) with an abstract property as the handler delegate:
interface IFoo
{
protected EventHandler BarHandler { get; set; }
event EventHandler Bar
{
add => BarHandler += value;
remove => BarHandler -= value;
}
}
class Foo : IFoo
{
EventHandler IFoo.BarHandler { get; set; }
}
So that somewhere else in a default method implementation could invoke the event:
var handler = BarHandler;
handler?.Invoke(this, e);