Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

dynamic

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.

like image 702
Sergiy Belozorov Avatar asked Sep 12 '13 13:09

Sergiy Belozorov


People also ask

Why does letter C exist?

The letter c was applied by French orthographists in the 12th century to represent the sound ts in English, and this sound developed into the simpler sibilant s.

Does the letter C exist?

C, or c, is the third letter in the English and ISO basic Latin alphabets. Its name in English is cee (pronounced /ˈsiː/), plural cees.

Why does C make 2 sounds?

In the Latin-based orthographies of many European languages, including English, a distinction between hard and soft ⟨c⟩ occurs in which ⟨c⟩ represents two distinct phonemes. The sound of a hard ⟨c⟩ often precedes the non-front vowels ⟨a⟩, ⟨o⟩ and ⟨u⟩, and is that of the voiceless velar stop, /k/ (as in car).

Why is C named so?

Quote from wikipedia: "A successor to the programming language B, C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to construct utilities running on Unix." The creators want that everyone "see" his language. So he named it "C".


1 Answers

 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

like image 66
Hans Passant Avatar answered Oct 26 '22 22:10

Hans Passant