1

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?

Community
  • 1
  • 1
arman
  • 649
  • 1
  • 10
  • 27
  • Tutorial uses `class`, you've declared an `interface`. C#8 has default interface methods, but I don't think that it's valid in your case – Pavel Anikhouski May 02 '20 at 11:34
  • exactly an interface cannot have an implementation – TAHA SULTAN TEMURI May 02 '20 at 11:35
  • @TAHASULTANTEMURI C#8 has default interface methods, but I don't think that's an OP case – Pavel Anikhouski May 02 '20 at 11:37
  • But in c# 8 we can create an interface with default implement https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods – arman May 02 '20 at 11:38
  • 2
    @sda2584541 Please, check the [https://stackoverflow.com/questions/58796545/event-inheritance-with-c8-default-interface-implementation-traits](https://stackoverflow.com/questions/58796545/event-inheritance-with-c8-default-interface-implementation-traits) question and github issue [2928](https://github.com/dotnet/csharplang/issues/2928). It seems to be an exactly your case – Pavel Anikhouski May 02 '20 at 11:45
  • @PavelAnikhouski, yeah event only work on the classes! not the default Interface Method (DIM). thank you. – arman May 02 '20 at 11:51

1 Answers1

5

Reason

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.

Solution

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);
Alsein
  • 4,268
  • 1
  • 15
  • 35