8

次のクラスを持つライブラリがあります。

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);
    }
}

そしてそれらを使用するプログラム:

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);
}

出力は

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

そして、私は例外を受け取ります

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

動的オブジェクトがプロパティFooに直接アクセスするのに、メソッドの呼び出しに失敗し、代わりにBarand を使用するのはなぜですか? アクセス修飾子は同じです。TryInvokeMemberTryGetMember

更新:説明されている動作は Mono で観察されます。Microsoft は、プロパティにアクセスするときに既に失敗していFooます。次のコードは意図したとおりに機能します。

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);
}

これがバグなのか機能なのかは、Microsoft によって決定されます。ただし、変数を動的に変換してもアクセスが制限されないはずです。

4

1 に答える 1

11
 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

于 2013-09-14T21:56:22.457 に答える