2013-09-12 3 views
6

У меня есть библиотека со следующими классами:Почему C# dynamic разрешает внутренние свойства, но не методы?

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, но не вызывает метод Bar и вместо этого использует TryInvokeMember и TryGetMember? Они имеют одинаковые модификаторы доступа.

Обновление: Описанное поведение наблюдается на 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); 
} 

Независимо от того, является ли это ошибкой или функцией, решение должно быть определено корпорацией Майкрософт. Однако я ожидаю, что преобразование переменной в динамическое не должно ограничивать доступ.

+6

Поскольку DynamicObject также имеет TryInvokeMember(), тот, который вы не переопределили, не может быть использован. –

+0

Но я не хочу его переопределять. Я хочу, чтобы 'Bar' выполнялся напрямую. Вот почему я определяю его явно. –

+0

Обновлен вопрос, чтобы сделать его более понятным. –

ответ

7
internal class Entity .... 

динамического ключевого слово не является обходным путем для ограниченного доступа. Класс Entity объявляется внутренним, поэтому, пытаясь вызвать его метод Bar() из кода, который не является частью сборки, в которой объект Entity живет, будет отклонен связующим, сообщение оставляет мало для воображения:

EntityLib.Entity.Bar (целое)»недоступен из-за его уровня защиты

логический способ получить вперед, чтобы объявить класс сущности общественного. Если по какой-то причине это проблема, вы можете нарушить правила с помощью Reflection. Вам нужно использовать BindingFlags.NonPublic | Параметры BindingFlag.Instance в вызове Type.GetMethod().

Что касается основного вопроса, я с радостью отчитаю это как ошибку. Связующее C# DLR невозможно перепроектировать. Не в последнюю очередь потому, что код для него не включен в справочный источник, Microsoft, похоже, рассматривает его как коммерческий секрет. Это. Вы можете указать его на странице connect.microsoft.com

+2

На самом деле я не пытаюсь нарушить правила доступа. Идея заключается в том, что пользователи должны иметь возможность вызывать методы, объявленные в открытом интерфейсе 'IEntity', который реализуется этим классом.В остальном следует использовать «TryGetMember» и «TryInvokeMember» (даже если он явно определен в классе «Entity»). Но я предполагаю, что реализация не справляется с такой ситуацией достаточно хорошо. –

+0

Подана ошибка: https://connect.microsoft.com/VisualStudio/feedback/details/800742/dynamic-keyword-restricts-access-to-members-exposed-via-public-interface –

Смежные вопросы