Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>What it sounds like you want to do is have your plugin instances typed to object or dynamic (versus having them typed to some interface where you effectively pass through the TryGetMember request) and then perform a dynamic binding against another object. Luckily for you the DLR interop protocol allows for exactly this scenario! It'll require to drop down to the IDynamicMetaObjectProvider layer instead of using DynamicObject but it's actually pretty simple. I'll show you a simple example using InvokeMember that works end-to-end w/ C# 4.0. You'll need to go and do the rest of the operations - in particular IronPython will use GetMember instead of InvokeMember. But that's a straight forward process.</p> <p>First an explanation of how you do it. At the IDMOP level the DLR deals with meta objects and languages request operations from meta objects and those meta objects return more meta objects. You can also ask the language to perform it's default binding and most importantly you can provide to it a suggestion of what to do when things go wrong.</p> <p>Based upon that you can ask the language to try and bind to each one of your plugins and your own object. Depending on whether you want plugins to have precedence, or your dynamic object, you can perform the binding to yourself last. This sample demonstrates both techniques based upon the member names.</p> <p>So without further delay, here it is:</p> <pre><code>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 &lt; 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]; } } } </code></pre> <p>Running this prints:</p> <p>MyDynamicObject Foo TestPlugin Bar</p> <p>Showing that for Foo members we prefer the binding on the actual object, and for Bar members we prefer Bar. If you add access to a third Baz member it produces C#'s runtime binder exception. If this was called from IronPython we'd produce an AttributeError to Python programs (a MissingMemberException in .NET) and a JavaScript implement should return undefined to their programs.</p> <p>So you get not only your extensible plugin system you also easily get the correct behavior in any langauge which consumes your object.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload