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