5

Let's say we have the following C# class

public class Class1
{
    protected event EventHandler ProtectedEvent;
    protected virtual void OverrideMe() { }
}

It seems to be impossible to use the ProtectedEvent in F#.

type HelpMe() as this =
    inherit Class1()

    do
        printfn "%A" this.ProtectedEvent        

    member x.HookEvents() =
        printfn "%A" x.ProtectedEvent

    member private x.HookEvents2() =
        printfn "%A" x.ProtectedEvent 

    override x.OverrideMe() =
        printfn "%A" x.ProtectedEvent 

In this example I have attempted to call printfn on it, as there are multiple ways to hook up events in F# and I wanted to be clear that is simply the referencing of the event at all that causes the problem.

In each of the cases above the compiler complains with the following error

A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope.

I understand this error, what causes it and its purpose. Usually, the work around is to wrap the call in a private member, which works fine with methods - but that does not seem to work with events. No matter what I try, it seems to be impossible to use protected events in F# unless I resort to doing something with reflection, or make some changes to the base class (which in my case is not possible).

Note that I have also tried all possible combinations of using base, this and x.

Am I doing something wrong ?

Ross McKinlay
  • 560
  • 4
  • 7
  • The backing field for an event is always private. There is no visible field to reference. You can't do this in C# either. – SLaks Dec 28 '14 at 16:51
  • Yes, you can. Inherit from `Class1` in C# and you will be able to hook to the event (this is a simplified example). My particular case is the `ToolBoxInitialized` event in `Microsoft.VisualStudio.Shell.Package` – Ross McKinlay Dec 28 '14 at 17:01
  • You can add and remove handlers, but you can't access the field. See http://blog.slaks.net/2011/07/about-net-events.html – SLaks Dec 28 '14 at 17:02
  • 1
    Ok I see our confusion. In F# you can reference events directly (it exposes the add / remove / subscribe etc methods). You can't do 'Console.WriteLine("{0}", this.ProtectedEvent);' in C# but you can write 'printfn "%A" this.ProtectedEvent' in F#. – Ross McKinlay Dec 28 '14 at 17:08
  • the point is that referencing the field at all causes the compiler error, not how I attempt to hook up the event using .add, |> Observable.subscribe or whatever – Ross McKinlay Dec 28 '14 at 17:10

1 Answers1

3

I suspect that there is something about the code that the compiler generates behind the scene when you treat the event as a first-class value that later confuses it (i.e. some hidden lambda function that makes the compiler think it cannot access the protected member). I'd say that this is a bug.

As far as I can see, you can workaround it by using add_ProtectedEvent and remove_ProtectedEvent members directly (they do not show in the auto-completion, but they are there and are accessible - they are protected, but calling them is a direct method call, which is fine):

type HelpMe() =
    inherit Class1()

    member x.HookEvents() =
        let eh = System.EventHandler(fun _ _ -> printfn "yay")
        x.add_ProtectedEvent(eh)

    override x.OverrideMe() =
        printfn "hello"

This compiled fine for me. It is a shame that you cannot use the protected event as a first-class value, but this at least lets you use it...

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • cheers Tomas. I should report this as a bug then. A co-worker who is new to F# hit this and was very frustrated not being able to do something that should be easy - not a good impression! – Ross McKinlay Dec 30 '14 at 13:18