用IronPython对象替换.NET对象实例

4 投票
1 回答
904 浏览
提问于 2025-04-17 22:06

我正在尝试做以下几件事:

  • 获取一个在C#中定义的对象实例
    • 这个对象实现了某个接口
    • 它是另一个C#类实例的一个字段
  • 用一个IronPython代理对象包裹它,这个代理会记录方法调用
  • 把这个包裹的对象放回原来的位置

下面是一个例子,展示我想要做的事情。实际上,IWoof接口上还有很多其他方法。

C#的类和接口:

namespace Foo
{
    public interface IWoof { string DoSomething(); }

    public class Bar
    {
        public Bar() { Woof = new WoofImp(); }
        public IWoof Woof { get; set; }
    }

    public class WoofImp : IWoof
    {
        public string DoSomething()
        {
            return "DoSomething executing in C#";
        }
    }

}

我想把bar.Woof替换成一个IronPython代理。我想通过一个IronPython脚本来实现这个。我尝试了各种方法。

第一次尝试:重写getattr

我定义了一个包装器,像这样:

class Wrapper(object):

    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, name):
        print '__getattr__ called'
        if name == "DoSomething":
           return self.__DoSomething
        else:
            return object.__getattr__(self, name)

    def __DoSomething(self):
        result = self._obj.DoSomething()
        return result + " AND DoSomething executing in Python"

这工作得很好,正如你所期待的那样:

import Foo
bar = Foo.Bar()
woof = Wrapper(bar.Woof)
print woof.DoSomething()

> DoSomething executing in C# AND DoSomething executing in Python

但是,当我尝试用包裹的对象替换bar.Woof时,它却不行:

bar.Woof = woof_wrapper

> TypeError: expected IWoof, got Object_1$1
> Microsoft.Scripting.ArgumentTypeException: expected IWoof, got Object_1$1
   at CallSite.Target(Closure , CallSite , Object , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)

第二次尝试:实现C#接口

显然,我的包装器需要实现IWoof接口,所以我尝试了这样做。

class Wrapper_IWoof(object, Foo.IWoof): # Inherits from IWoof
... etc

现在我可以添加包装器实例并尝试这样调用它:

woof = Wrapper_IWoof(bar.Woof)
bar.Woof = woof
print bar.Woof.DoSomething()

但我遇到了一个新问题:

> AttributeError: 'Wrapper_IWoof' object has no attribute 'DoSomething'
> System.MissingMemberException: 'Wrapper_IWoof' object has no attribute 'DoSomething'
   at IronPython.Runtime.Operations.PythonOps.MissingInvokeMethodException(Object o, String name)
   at IronPython.NewTypes.System.Object#IWoof_4$4.DoSomething()
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`2.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run3[T0,T1,T2,TRet](T0 arg0, T1 arg1, T2 arg2)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Microsoft.Scripting.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)

第三和第四次尝试:动态代理

我还尝试了按照这里描述的方法(同样实现IWoof): http://www.ronnie-midnight-oil.net/2008/11/checking-type-contract-from-ironpython.html 不过,这些尝试也遇到了之前两个尝试中的相同问题。

替代解决方案

解决同样问题的另一种方法是通过反射生成一个python类并使用它。我已经这样做了,并且成功了。这给了我下面Jeff Hardy所描述的明确函数声明。

但是,我真的很想知道为什么之前的尝试没有成功。我是做错了什么,还是遗漏了什么?

1 个回答

0

我觉得你用__getattr__把事情搞得太复杂了。试着直接实现这个接口:

class Wrapper_IWoof(object, Foo.IWoof):

    def __init__(self, obj):
        self._obj = obj

    def DoSomething(self):
        result = self._obj.DoSomething()
        return result + " AND DoSomething executing in Python"

撰写回答