48

This compiles:

public interface IMyInterface
{
    event Action<dynamic> OnSomeEvent;
}

class MyInterface : IMyInterface
{
    public event Action<dynamic> OnSomeEvent;
}

But when i separate the interface and the implementation to different projects, i get:

Accessor 'TestProject2.MyInterface.OnSomeEvent.remove' cannot implement interface member 'InterfaceNamespace.IMyInterface.remove_OnSomeEvent(System.Action)' for type 'TestProject2.MyInterface'. Use an explicit interface implementation.

This occurs only with a dynamic parameter...

Femaref
  • 60,705
  • 7
  • 138
  • 176
seldary
  • 6,186
  • 4
  • 40
  • 55

3 Answers3

30

Good catch. This looks like it's possibly a bug in the C# compiler - I'll ping Eric Lippert to see what he thinks. (dynamic can be a bit tricksy; there may well be a perfectly good but non-obvious reason for this error.)

EDIT: The code below appears not to work after all. I could have sworn I had it working this morning... I'm very confused as to what's going on. As per Simon's comments, the code fails with a message saying it's not supported by the language.

Note that if you do use explicit interface implementation, it appears to compile just fine:

// Doesn't actually compile - see edit above
class MyInterface : IMyInterface
{
    private Action<dynamic> foo;

    event Action<dynamic> IMyInterface.OnSomeEvent
    {
        // TODO (potentially): thread safety
        add { foo += value; }
        remove { foo -= value; }
    }
}

EDIT: The rest of this answer still stands...

Note that you can't specify a field-like event as an explicitly-implemented event, i.e. this doesn't work:

event Action<dynamic> IMyInterface.OnSomeEvent;

It gives the following error message:

Test.cs(15,39): error CS0071: An explicit interface implementation of an event must use event accessor syntax

And if you just try to change to the event accessor syntax, you get the same error as the original code.

Note that changing the event to a property works fine with an auto-implemented property implementation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I wonder how the Compiler would know how to protect overriding and then recognize what to do with dynamic when you compile the projects apart from each other. – Caspar Kleijne May 08 '11 at 10:03
  • Is there anything to configure to have the explicit interface implementation work? I get a `error CS0682: 'ConsoleApplication1.MyInterface.ClassLibrary1.IMyInterface.OnSomeEvent' cannot implement 'ClassLibrary1.IMyInterface.OnSomeEvent' because it is not supported by the language` error trying to compile. – Simon Mourier May 08 '11 at 10:28
  • @Simon: What version of the C# compiler are you using? Dynamic was introduced in C# 4. – Jon Skeet May 08 '11 at 12:27
  • @Jon - yes of course, I'm using VS 2010 (SP1) with C# 4. The 'dynamic' keyword is perfectly understood by the compiler. But I can't compile your code above in a separate .exe console app when IMyInterface is defined in another class library. Line with the original question, if works fine in the same project. – Simon Mourier May 08 '11 at 16:41
  • @Simon: That's very strange... it works fine on my box, also SP1. The message looks a little odd with a name of `ConsoleApplication1.MyInterface.ClassLibrary1.IMyInterface.OnSomeEvent`... are you sure you don't have another `IMyInterface` interface somewhere? – Jon Skeet May 08 '11 at 17:17
  • @Jon - I suppose the full name comes from the fact the implementation is private (explicit). I must miss something, but can't find what, that's why I asked :-) If you have 5 minutes to spare, I've rar'd my VS project, it can be downloaded from here: http://www.filedropper.com/consoleapplication3c4 – Simon Mourier May 08 '11 at 17:29
  • @Simon: Okay, that's really bizarre... it's failing for me too now, even on the code where it was working this morning. Will investigate further... – Jon Skeet May 08 '11 at 17:46
  • 1
    @Caspar: Sorry, I've only just noticed your comment. Could you go into more details? Note that it's okay for a *property* of type `Action.` – Jon Skeet May 09 '11 at 05:32
  • @Jon, A bit complex for a comment. If the delegates have different runtime type they are never equal, so the cannot be removed (it needs to do a lookup) but explicit interface implementation makes them the same Type. However in the same assembly the compiler "does magic"?? or recognizes the same type? – Caspar Kleijne May 09 '11 at 07:54
  • + However the event has to be public but the explicit interface implementation cannot agree with that. Seems like it is running in circles. – Caspar Kleijne May 09 '11 at 08:32
  • 1
    @Caspar: the "magic" that is supposed to happen here is that an Action is turned at compile time into Action. We don't make any attempt to treat it as Action or Action or whatever, depending on the runtime type of the thing assigned to it. "dynamic" is just "object" in fancy clothes. – Eric Lippert May 09 '11 at 17:27
  • @Jon see my answer, I elaborated it. (otherwise I look too vague ;) – Caspar Kleijne May 09 '11 at 19:31
9

Thanks for posting this question, and thanks to Jon for sending it my way. I've put this in the investigation queue of one of our testers who specializes in "dynamic". We'll see if we can figure out what's going on here. It certainly smells like a bug.

In the future, consider posting stuff like this on connect.microsoft.com; that gets it to testers even faster, and gives us a better mechanism for getting more information about the issue.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • the fun part from this whole problem, that if you pass `dynamic` trough a generic T it compiles fine.so public `interface IMyInterface{event Action OnSomeEvent;}` and its correct implementation that delivers is a `ns.IMyInterface = new class MyInterface()` compiles, even if the interface is in a different assembly. Wrapping the `dynamic` in a T is different/stricter than using it directly?? – Caspar Kleijne May 09 '11 at 18:45
  • see my answer, I elaborated it. – Caspar Kleijne May 09 '11 at 19:31
2

This answer is to elborate my thoughts on this interesting problem. Not a real answer, but a contribution to the whole discussion that is too small for a normal comment.

I checked a few things, this interface:

namespace DifferentAssemblyNamespace
{
    public interface IBar
    {
        event Action<dynamic> OnSomeEvent;
    }
}

and its implementation:

// implicit interface implementation
// generates compile error "Explicit interface implementation"
public class Foo1 : IBar
{
    private Action<dynamic> foo;
    public event Action<dynamic> OnSomeEvent
    {
        add { foo += value; }
        remove { foo -= value; }
    }
}

// implicit interface implementation
// generates compile error "Not supported by the language"
public class Foo2 : IBar
{
    private Action<dynamic> foo;

    event Action<dynamic> IBar.OnSomeEvent
    {
        add { foo += value; }
        remove { foo -= value; }
    }
}

will never work, seems that one rule is excluding the other necessary rule.

but.. if we call generics for help, and use a Type parameter instead of using dynamic directly like:

namespace DifferentAssemblyNamespace
{
    public interface IGenericBar<T>
    {
        event Action<T> OnSomeEvent;
    }
}

and its implementation.

// implicit interface implementation
public class Foo3<T> : IGenericBar<T>
{
    private Action<T> foo;

    event Action<T> IGenericBar<T>.OnSomeEvent
    {
        add { foo += value; }
        remove { foo -= value; }
    }
}

for some reason we can build (as it should) and run:

/** does build **/
IGenericBar<dynamic> f = new Foo3<dynamic>();
f.OnSomeEvent += new Action<dynamic>(f_OnSomeEvent);

seems that the Type Parameter does something extra that the compiler is happy with.

I am not sure what is going on, so I would like to know as well.

assumption, highly hypothetical (perhaps crap)

but currently I put my two cents on the comparison of types there must be made via the add/remove accessors in the linked list that holds the target/methods of the event.

I bet that the compiler falls over the problem that it cannot guarantee what dynamic is in the external assembly, thus cannot determine if an element is already in the list or not, which is necessary to add or remove them.(Hence explicit interface implementation)

We all know it is just some of a attributed object but it still seems that it needs an extra step where some strong-type is guaranteed, and that is what T does, at compile time.

/ assumption, highly hypothetical (perhaps crap)

Community
  • 1
  • 1
Caspar Kleijne
  • 21,552
  • 13
  • 72
  • 102