0

Related to: C#: Raising an inherited event

When trying to raise an event defined in an inherited class (implemented in C#), the compiler throws a BC31132 error which states:

'RaiseEvent' definition missing for event Event

The help article linked above says:

If an event is declared as Custom, it must supply a procedure for raising the event.

However, the Object Browser doesn't show that the event is implemented as Custom:

enter image description here

Why is VB.Net complaining about a custom event when I haven't defined it as such?

Example

Parent C# code that implements the base class to be inherited:

public class EventGenerator
    {
        public event EventHandler SomethingHappened;

        public void SampleMethod()
        {
            // Event is invoked without issue.
            SomethingHappened?.Invoke(this, new EventArgs());
        }
    }

Child VB.Net class that inherits the base class and attempts to call the event:

Public Class TestClientClass
    Inherits EventGenerator

    Public Sub TestRaiseInheritedEvent()
        ' BC31132
        RaiseEvent SomethingHappened()
    End Sub
End Class

Further reading

https://learn.microsoft.com/en-us/dotnet/standard/events/ https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/ http://jmcilhinney.blogspot.com/2009/11/defining-and-raising-custom-events.html

gcode
  • 2,954
  • 4
  • 21
  • 32
  • 2
    Does the C# class provide a protected method calld `OnValueChanged` ? Unless the base class exposes a way to raise an event that it defined, you can't raise it. Shadowing doesn't actually work because the events are entirely separate; the base class can't raise the shadowing event and you can't raise the event for any subscriptions added through a base class reference. – madreflection Dec 04 '22 at 22:49
  • 2
    You should read [this](http://jmcilhinney.blogspot.com/2009/11/defining-and-raising-custom-events.html). – jmcilhinney Dec 04 '22 at 23:20
  • Note the "define" part of the C# code you posted. – Craig Dec 05 '22 at 14:43
  • @madreflection and jmcilhinney thank you for the info. For my understanding, I'm trying to find some official documentation/guidelines about this protected *On* method that is required to be defined, and all I can find is [this](https://learn.microsoft.com/en-us/dotnet/standard/events/) Learn article that starts with *Typically* when discussing implementing that method. If I don't care about special logic when raising the event (just raise it), does the method really need to be defined? – gcode Dec 06 '22 at 00:14
  • 1
    The event member itself is just a contract for adding/removing subscriptions, like how a property is a contract for getting/setting a value. You can't *"just raise it"*, as it requires knowledge of how those subscriptions were stored. The "special logic" means selecting which subscriptions to invoke and then invoking those. Sometimes, it's a private field, but it could also be an instance taken from a collection, like the `Events` property provided by the `Component` class. Again, unless the base class provides a way to do that, you're out of luck. – madreflection Dec 06 '22 at 00:38
  • 1
    **Shadow**ing is never the answer. – Enigmativity Dec 06 '22 at 00:56
  • 1
    I can't replicate the issue you're seeing. Can you please provide enough code for us to see the issue? – Enigmativity Dec 06 '22 at 01:02
  • @madreflection Thank you again for the detailed explanation, and I have to apologize since I have not done a good job of providing an example of the issue I'm facing, which I think is leading to some confusion. I spent some more time pouring over the Learn documentation, when I came across an article that described exactly what I'm trying to do: How to raise base class events in derived classes. I came across a rule which maybe you were explaining: "... consider the fact that **events are a special type of delegate** that can only be invoked from within the class that declared them." – gcode Dec 07 '22 at 01:11
  • I think you did an excellent job explaining how events work in the background, by the way. I just didn't fully appreciate how unique `event`s are. You made the `Property` comparison which may have thrown me, as you can still use base class properties from inherited classes without any additional work as far as I know. Feel free to transfer your comments into an answer and I'd be happy to mark as accepted. – gcode Dec 07 '22 at 01:19
  • @Enigmativity I apologize, I could've done a much better job creating an example. I made a (GitHub repo)[https://github.com/gbakeman/TestNetFWEvent] that attempts to succinctly demonstrate the issue I was facing. I'll try to update my OP as well. – gcode Dec 07 '22 at 01:21
  • 1
    *"you can still use base class properties from inherited classes"* - And you can still add and remove event subscriptions to base class events from inherited classes. That's apples to apples because those are the *public* contracts provided for interacting with those members. Properties don't have additional things you can do, and events don't expose raising the event as part of the contract. – madreflection Dec 07 '22 at 01:33
  • 1
    That said, there's a `.fire` metadata slot for events, along with `.addon` and `.removeon` that correlate to the `add` and `remove` accessors, (properties have `.get` and `.set` for `get` and `set` accessors). When an event in VB is a `Custom Event`, it allows not just the `AddHandler` and `RemoveHandler` blocks (accessors, `add` and `remove` in C#), but also `RaiseEvent`. The `RaiseEvent` block becomes a private `raise_EventName` method, which is comparable to the protected `OnEventName` method mentioned in the article. C# doesn't support `.fire`. – madreflection Dec 07 '22 at 01:48

1 Answers1

0

It doesn't matter if this is C#, VB.NET, or a combination of both, you simply cannot do this in .NET:

Public Class Foo
    Public Event Bar As EventHandler

End Class

Public Class Qaz
    Inherits Foo

    Private Sub RaiseBarFromQaz()
        RaiseEvent Bar(Me, New EventArgs())
    End Sub
End Class

It gives me the error: BC30029 Derived classes cannot raise base class events.

The C# version of this error is: CS0070 The event 'Foo.Bar' can only appear on the left hand side of += or -= (except when used from within the type 'Foo').

You must include a Protect Sub/protected void in the parent class to allow child classes to raise the event.

It looks like this:

Public Class Foo
    Public Event Bar As EventHandler

    Protected Sub RaiseBarFromFoo()
        RaiseEvent Bar(Me, New EventArgs())
    End Sub
End Class

Public Class Qaz
    Inherits Foo

    Private Sub RaiseBarFromQaz()
        RaiseBarFromFoo()
    End Sub
End Class

Now, the use of Shadow is bad. Let's see why.

Sub Main
    Dim qaz As New Qaz
    Dim foo As Foo = qaz
    AddHandler foo.Bar, Sub (s, e) Console.WriteLine("Bar")
    qaz.RaiseBarFromQaz()
    foo.RaiseBarFromFoo()
End Sub

Public Class Foo
    Public Event Bar As EventHandler

    Public Sub RaiseBarFromFoo()
        RaiseEvent Bar(Me, New EventArgs())
    End Sub
End Class

Public Class Qaz
    Inherits Foo

    Public Shadows Event Bar As EventHandler

    Public Sub RaiseBarFromQaz()
        RaiseEvent Bar(Me, New EventArgs())
    End Sub
End Class

When I run this, even though I call RaiseEvent twice I only get one event raised. Well, in fact both Bar events are raised once, but the Shadow makes it that references to Foo don't see the event in Qaz.

Let's look at the correct code for this:

Sub Main
    Dim qaz As New Qaz
    Dim foo As Foo = qaz
    AddHandler foo.Bar, Sub(s, e) Console.WriteLine("Bar")
    qaz.RaiseBarFromQaz()
    foo.RaiseBarFromFoo()
End Sub

Public Class Foo
    Public Event Bar As EventHandler

    Public Sub RaiseBarFromFoo()
        RaiseEvent Bar(Me, New EventArgs())
    End Sub
End Class

Public Class Qaz
    Inherits Foo

    Public Sub RaiseBarFromQaz()
        RaiseBarFromFoo()
    End Sub
End Class

Running that gets two Bar written to the console. It works as expected.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Good answer, thank you. Weird that I wasn't getting the BC30029 error like you, that sure would've given me a hint! – gcode Dec 08 '22 at 18:46