2

В настоящее время я принимаю IronPython в приложении .NET 2.0.Делегирование разрешения динамического объекта в другие экземпляры

Я хочу создать класс (на C#), экземпляры которого могут быть «расширены» экземплярами плагина. Это означает, что каждый доступ к члену, который не может быть разрешен в моем экземпляре, должен быть перенаправлен в соответствующий экземпляр модуля, предоставляющий этот член. Тогда мой объект будет содержать закрытый контейнер с этими экземплярами плагина.

AFAICS, путь оттуда происходит через DynamicObject. Первый шаг был простым до тех пор, когда TryGetMember вызывается всякий раз, когда код python использует «неизвестных» членов моего экземпляра. Я также мог возвращать объекты и делегаты, которые можно было бы использовать из кода python.

Но каким-то образом я застрял при попытке использовать DLR для выполнения «подзапроса» в экземпляре плагина и e. G. возвращает метод или свойство экземпляра плагина в том, как его ожидает IronPython.

Любые подсказки приветствуются!

Спасибо!

Редактировать: Мой первоначальный вопрос не был сформулирован достаточно ясно, извините. Вот несколько вопросов:

  • Решение должно работать с простым .NET 2.0, без использования .NET 3.5 или 4.0.
  • Список плагинов для каждого экземпляра (это означает, что каждый экземпляр может иметь другой, но неизменяемый список объектов плагина).
  • Объекты плагина должны быть прозрачными объектами C# со всеми открытыми элементами (или, по крайней мере, с методами и свойствами).
  • Обнаружение столкновений не имеет значения.

Еще раз спасибо.

+0

Я не уверен, что понял вопрос. Позвольте мне убедиться, что я прав. У вас есть класс Foo, который наследуется от DynamicObject. У Foo нет свойства «Bar», поэтому в вашем TryGetMember вы хотите вернуть значение, предоставляемое плагином? Что вы подразумеваете под «подходом»? – Amy

+0

Hi, yodaj007! Ваше понимание верное. Мой класс Foo должен быть «расширяемым» объектами плагина, предоставленными третьими сторонами. Методы и свойства, не найденные в моем классе, должны быть найдены в экземплярах объекта плагина. Бонусные очки, если эти классы плагина могут быть произвольными объектами .NET. Наследование от DynamicObject просто показалось мне для меня. Я также прочитал еще кое-что по этому вопросу, возможно, я пею, чтобы создать свой собственный CallSite ... – MarkusSchaber

ответ

3

Похоже, что вы хотите, чтобы ваши экземпляры плагинов были введены в объект или динамические (по сравнению с тем, что они набрали какой-либо интерфейс, где вы действительно проходите через запрос TryGetMember), а затем выполните динамическое привязку к другому объекту. К счастью для вас протокол DLP interop позволяет именно этот сценарий! Для того, чтобы использовать DynamicObject, вам нужно будет спуститься к слою IDynamicMetaObjectProvider, но на самом деле это довольно просто. Я покажу вам простой пример с помощью InvokeMember, который работает сквозным w/C# 4.0. Вам нужно будет выполнять остальные операции - в частности, IronPython будет использовать GetMember вместо InvokeMember. Но это прямой процесс.

Сначала объясните, как вы это делаете. На уровне IDMOP DLR обрабатывает метаобъекты и запросы на запросы от метаобъектов, а эти метаобъекты возвращают больше мета-объектов. Вы также можете попросить язык выполнить привязку по умолчанию и, самое главное, вы можете предложить ему предложение о том, что делать, если что-то пойдет не так.

Исходя из этого вы можете попросить язык попробовать и привязать к каждому из ваших плагинов и вашего собственного объекта. В зависимости от того, хотите ли вы иметь приоритеты плагинов или ваш динамический объект, вы можете выполнить привязку к себе в последний раз. В этом примере демонстрируются оба метода, основанные на именах участников.

Итак, без дальнейших проволочек, вот оно:

using System; 
using System.Dynamic; 
using System.Linq.Expressions; 

namespace ConsoleApplication10 { 
    class Program { 
     static void Main(string[] args) { 
      dynamic dynamicObj = new MyDynamicObject(new TestPlugin()); 
      dynamicObj.Foo(); 
      dynamicObj.Bar(); 
      Console.ReadLine(); 
     } 

    } 

    public class TestPlugin { 
     public void Foo() { 
      Console.WriteLine("TestPlugin Foo"); 
     } 

     public void Bar() { 
      Console.WriteLine("TestPlugin Bar"); 
     } 
    } 

    class MyDynamicObject : IDynamicMetaObjectProvider { 
     internal readonly object[] _plugins; 

     public void Foo() { 
      Console.WriteLine("MyDynamicObject Foo"); 
     } 

     public void Bar() { 
      Console.WriteLine("MyDynamicObject Bar"); 
     } 

     public MyDynamicObject(params object[] plugins) { 
      _plugins = plugins; 
     } 

     class Meta : DynamicMetaObject { 
      public Meta(Expression parameter, BindingRestrictions restrictions, MyDynamicObject self) 
       : base(parameter, restrictions, self) { 
      } 

      public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {     
       // get the default binding the language would return if we weren't involved 
       // This will either access a property on MyDynamicObject or it will report 
       // an error in a language appropriate manner. 
       DynamicMetaObject errorSuggestion = binder.FallbackInvokeMember(this, args); 

       // run through the plugins and replace our current rule. Running through 
       // the list forward means the last plugin has the highest precedence because 
       // it may throw away the previous rules if it succeeds. 
       for (int i = 0; i < Value._plugins.Length; i++) { 
        var pluginDo = DynamicMetaObject.Create(Value._plugins[i], 
         Expression.Call(
          typeof(MyDynamicObjectOps).GetMethod("GetPlugin"), 
          Expression, 
          Expression.Constant(i) 
         ) 
        ); 

        errorSuggestion = binder.FallbackInvokeMember(pluginDo, args, errorSuggestion);      
       } 

       // Do we want DynamicMetaObject to have precedence? If so then we can do 
       // one more bind passing what we've produced so far as the rule. Or if the 
       // plugins have precedence we could just return the value. We'll do that 
       // here based upon the member name. 

       if (binder.Name == "Foo") { 
        return binder.FallbackInvokeMember(this, args, errorSuggestion); 
       } 

       return errorSuggestion; 
      } 

      public new MyDynamicObject Value { 
       get { 
        return (MyDynamicObject)base.Value; 
       } 
      } 
     } 



     #region IDynamicMetaObjectProvider Members 

     public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter) { 
      return new Meta(parameter, BindingRestrictions.Empty, this); 
     } 

     #endregion 
    } 

    public static class MyDynamicObjectOps { 
     public static object GetPlugin(object myDo, int index) { 
      return ((MyDynamicObject)myDo)._plugins[index]; 
     } 
    } 
} 

Запуск Печатается:

MyDynamicObject Foo TestPlugin Бар

Показывая, что для членов Foo мы предпочитаем связывание на реальном объекте , а для членов клуба мы предпочитаем Bar. Если вы добавите доступ к третьему члену Baz, он выдает исключение связующего звена C#. Если бы это было вызвано из IronPython, мы создавали программы AttributeError для Python (исключение MissingMemberException в .NET), и реализация JavaScript должна возвращаться не определенно к их программам.

Таким образом, вы получаете не только свою расширяемую систему плагинов, но и легко получаете правильное поведение в любом langauge, который потребляет ваш объект.

+0

FYI: Я использую IronPython 2.6.2, FileVersion 2.6.1008.2, Version 2.6.10920.0. (Я использовал 2.6.1 для своих первых тестов, потому что http://www.ironpython.net/ объявляет 2.6.1 в качестве текущей версии, но, похоже, обновление ничего не изменило.) – MarkusSchaber

+0

После очистки GAC (который ввел .NET .NET-сборки в мой .NET 4.0-решение, что привело к «смешным» эффектам), я могу сказать следующее: вызов через динамические работы C# 4.0, как рекламируется, но вызов через IronPython не работает - мой метод BindGetMember (который 1: 1, привязанный к вашему BindInvokeMember) всегда привязывается к методам плагинов, он не находит методы, предоставляемые основным объектом. Результат такой же с чистым .NET 2.0 решением, а также с Mono (linux). – MarkusSchaber

+0

Итак, текущее состояние заключается в том, что решение не работает для IronPython в .NET 4.0, 2.0 и Mono. Поскольку привязка к экземплярам плагина, кажется, работает нормально, моим текущим обходным решением является предоставление даже функциональности по умолчанию в виде такого плагина, чего я не хотел, но единственное решение, которое я могу придумать прямо сейчас. Меня все еще интересует лучшее решение, и я буду исследовать самостоятельно, но он работает «на данный момент». Спасибо за ваши усилия! (Я также очистил некоторые из моих старых, теперь устаревших комментариев здесь.) – MarkusSchaber

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

  • Нет связанных вопросов^_^