Reputation: 167
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
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
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