将动态对象分辨率委派给其他实例
-
16-10-2019 - |
题
我目前在.NET 2.0应用程序中托管Ironpython。
我想创建一个类(在C#)的类,其实例可以通过插件实例“扩展”。这意味着,在我的实例上无法解决的每个成员访问应转发给适当的插件实例,以提供该成员。然后,我的对象将使用这些插件实例容纳一个私人容器。
AFAIC,去那里的方式是通过dynamicobject派生而来的。到目前为止,第一步很容易,只要Python代码使用我实例的“未知”成员,就会称呼TrygetMember。我还可以返回可以从Python代码中使用的对象和委托。
但是,不知何故,当尝试使用DLR在插件实例和e上执行“ subsearch”时,我被卡住了。 G. Ironpython期望的方式返回插件实例的方法或属性。
欢迎任何提示!
谢谢!
编辑:对不起,我最初的问题还不够清楚。这里有一些要点:
- 该解决方案必须使用普通的.NET 2.0,no .NET 3.5或4.0允许运行。
- 插件列表为每个实例(这意味着每个实例可以具有不同的 - 但不可变的 - 插件对象列表)。
- 插件对象应为所有公共成员(或至少方法和属性)映射的普通c#对象。
- 碰撞检测并不重要。
再次感谢。
解决方案
听起来您想做的是将插件实例输入到对象或动态(与将它们键入某个接口,在该接口中有效地通过TryGetMember请求),然后对另一个对象执行动态绑定。幸运的是,DLR Interop协议允许这种情况!它需要下降到iDynamicMetaObjectProvider层,而不是使用DynamicObject,但实际上很简单。我将向您展示一个简单的示例,使用InvokeMember,该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 bar
表明对于FOO成员,我们更喜欢对实际对象的绑定,而对于酒吧成员,我们更喜欢bar。如果添加了对第三个BAZ成员的访问,则会产生C#的运行时活页夹异常。如果这是从Ironpython调用的,我们将生成Python程序(.NET中缺少的Memberexception)的属性,并且JavaScript Imellions应该返回未定义的程序。
因此,您不仅可以获得可扩展的插件系统,还可以轻松地在任何消耗对象的langauge中获得正确的行为。