Sergiy Belozorov
Sergiy Belozorov

Reputation: 6074

Why does C# dynamic resolves internal properties but not methods?

I have a library with the following classes:

public interface IEntity
{
    string Foo { get; }
    void Bar(int x);
}

public class EntityFactory 
{
    public static IEntity createEntity() 
    {
        return new Entity();
    }
}

internal class Entity : DynamicObject, IEntity
{
    public void Bar(int x)
    {
        Console.WriteLine("inside Bar");
        Console.WriteLine("bar {0}", x);
    }

    public string Foo
    {
        get 
        {
            Console.WriteLine("inside Foo getter");
            return "foo";
        }
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        Console.WriteLine("inside TryInvokeMember(binder.Name = '{0}')", binder.Name);
        return base.TryInvokeMember(binder, args, out result);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        Console.WriteLine("inside TryGetMember(binder.Name = '{0}')", binder.Name);
        if (binder.Name == "SomeVar")
        {
            result = 42;
            return true;
        }
        return base.TryGetMember(binder, out result);
    }
}

and a program that uses them:

public static void Main(string[] args)
{
    dynamic entity = EntityFactory.createEntity();
    Console.WriteLine("entity.Foo = {0}", entity.Foo);
    entity.Bar(24);
    Console.WriteLine("entity.SomeVar = {0}", entity.SomeVar);
}

The output is

inside Foo getter
entity.Foo = foo
inside TryInvokeMember(binder.Name = 'Bar')
inside TryGetMember(binder.Name = 'Bar')

and then I receive an exception

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: `EntityLib.Entity.Bar(int)' is inaccessible due to its protection level

Why does the dynamic object accesses property Foo directly, but fails to invoke method Bar and uses TryInvokeMember and TryGetMember instead? They have the same access modifiers.

Update: The described behavior is observed on Mono. Microsoft fails already when accessing Foo property. The following code works as intended:

public static void Main(string[] args)
{
    var entity = EntityFactory.createEntity();
    entity.Bar(24);
    Console.WriteLine("entity.Foo = {0}", entity.Foo);

    dynamic e = entity;
    Console.WriteLine("entity.SomeVar = {0}", e.SomeVar);
}

Whether this is a bug or a feature is to be decided by microsoft. However, I would expect that converting variable to dynamic should not restrict the access.

Upvotes: 8

Views: 3227

Answers (1)

Hans Passant
Hans Passant

Reputation: 941635

 internal class Entity ....

The dynamic keyword is not a workaround for restricted accessibility. The Entity class is declared internal, so trying to call its Bar() method from code that is not part of the assembly that Entity lives in is going to be rejected by the binder, the message leaves little to the imagination:

EntityLib.Entity.Bar(int)' is inaccessible due to its protection level

The logical way to get ahead is to declare the Entity class public. If that's a problem for some reason then you can break the rules with Reflection. You'll need to use BindingFlags.NonPublic | BindingFlag.Instance options in the Type.GetMethod() call.

As to the core question, I'll happily dismiss that as a bug. The C# DLR binder is impossible to reverse-engineer. Not in the least because the code for it isn't included in the Reference Source, Microsoft does appear to treat it like a trade secret. It is. You can file it at connect.microsoft.com

Upvotes: 11

Related Questions