mark
mark

Reputation: 167

C# Event Handler not Called when Triggered in F#

I'm struggling with exposing an event to C# from a class written in F#. When the Event is defined using a let binding this is no problem:

let myFSharpEvent = new Event<EventArgs>()

[<CLIEvent>]
member this.FSharpEvent = myFSharpEvent.Publish

member this.RaiseFSharpEvent e = myFSharpEvent.Trigger e

But when the Event is defined as private member it's compiling, but the event handler added at runtime is not executed.

member private this.myFSharpEvent = new Event<EventArgs>()

[<CLIEvent>]
member this.FSharpEvent = this.myFSharpEvent.Publish

member this.RaiseFSharpEvent e = this.myFSharpEvent.Trigger e

The C# code registering and calling the event looks like this:

class Program
{
    static void Main(string[] args)
    {
        var fsObject = new FSharpClass();
        Console.WriteLine(fsObject.ToString());

        fsObject.FSharpEvent += FsObject_FSharpEvent;
        fsObject.RaiseFSharpEvent(EventArgs.Empty);
        fsObject.FSharpEvent -= FsObject_FSharpEvent;

        Console.ReadLine();
    }

    private static void FsObject_FSharpEvent(object sender, EventArgs args)
    {
        Console.WriteLine("F# event was raised.");
    }
}

I found no way to work with the let binding however, becaus in my real-world scenario my class inherits from a class written in C# and has not only a default constructor, but also a copy-constructor and a deserialization constructor that I need to override.

So the question is: Why is the let binding working, but the member private not.

Upvotes: 4

Views: 184

Answers (2)

neil danson
neil danson

Reputation: 1542

The problem is that every time you access myFsharpEvent as a member you are creating a new instance. The let binding is only creating it once.

So when you call Trigger it’s a different instance than the one you are publishing, which is why it’s in raising it in you c# code.

Upvotes: 1

EricP
EricP

Reputation: 502

I like to use SharpLab.io to quickly see how some F# compiles to c#.

//Your F#
let myFSharpEvent = new Event<EventArgs>()

//To c#
internal FSharpEvent<EventArgs> myFSharpEvent;

-----------

//Your F#
member private this.myFSharpEvent = new Event<EventArgs>()

//To c#
internal FSharpEvent<EventArgs> myFSharpEvent
{
    get
    {
        return new FSharpEvent<EventArgs>();
    }
}

You can see that every time you call the private version, you're getting a new event handler which is then discarded to GC.

I find typical OO stuff confusing in F# as well, but I think the main pattern is to use let bindings for backing fields and wrap them with member bindings:

https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/members/let-bindings-in-classes

Upvotes: 2

Related Questions