IronPython可以动态地为c#类添加方法吗?

本文关键字:添加 方法 动态 IronPython | 更新日期: 2023-09-27 18:18:03

我有一些在嵌入式IronPython脚本中使用的简单c#类型:

//simple type
public class Foo
{
  public int a;
  public int b;
}
var engine = IronPython.Hosting.Python.CreateEngine();
dynamic scope = engine.CreateScope();
scope.foo = new Foo{ a = 1, b = 2 };
engine.Execute( "print( foo.a )", scope );

我想添加一些功能到Foo,但我不能修改它的代码。此外,我宁愿不从它派生,也不使用扩展方法,而是使用委托。理想情况下,我想写像

这样的东西
scope.AddMemberFunction( scope.foo, "Fun",
  new Func<Foo,int>( f => return f.a + f.b ) );

,然后直接在Python脚本中使用:

print( foo.Fun() )

我认为这正是使用Operations可以做到的,但这会引发一个异常,说'Foo'对象没有属性'Fun'

engine.Operations.SetMember( scope.foo, "Fun",
  new Func<Foo, int>( f => f.a + f.b ) );

接下来我尝试了Python的方式(说Foo在一个命名空间IronPythonApp中,它的汇编被添加到engine.Runtime中):

import IronPythonApp
def Fun( self ):
  return self.a + self.b
IronPythonApp.Foo.Fun = Fun

,但这给出了一个类似的异常:不能设置内置/扩展类型'Foo'的属性

是否有办法修改ironPython内部生成的Python类定义?

我根据Jeff Hardy的回答探索了一些选项。这里有两种方法都可以很好地扩展(这里只展示了简单的方法)

ExpandoObject !

var foo = new Foo { a = 1, b = 2 };
dynamic exp = new ExpandoObject();
exp.Fun = new Func<int>( () => foo.a + foo.b );
exp.a = foo.a; //automating these is not a big deal
exp.b = foo.b; //
//now just add exp to the scope and done.

使用SetMember

scope.origFoo = new Foo { a = 1, b = 2 };
  //the only variable thing in this string is 'origFoo'
  //so it's no big deal scaling this
engine.Execute( @"
  class GenericWrapper( object ) :
    def __init__( self, foo ):
    self.foo = foo
  def __getattr__( self, name ) :
    return getattr( self.foo, name )
  foo = GenericWrapper( origFoo )", scope );
   //ha, now scope contains something that we can mess with after all
engine.Operations.SetMember( scope.foo, "Fun",
  new Func<int>( () => scope.foo.a + scope.foo.b ) );
engine.Execute( "print( foo.Fun() )", scope );

IronPython可以动态地为c#类添加方法吗?

简短的回答:不,你不能修改。net类。默认情况下,它们没有必要的动态钩子来添加成员。

最好的方法是包装类并添加所需的成员;Python让这变得很简单:

class FooWrapper(object):
  def __init__(self, foo):
    self.foo = foo
  def __getattr__(self, name):
    return getattr(self.foo, name)
  def mymethod(self):
    ...

__getattr__特殊方法只调用不属于正常属性查找的属性,因此mymethod()将被正常查找,但其他任何内容将被转发到底层对象。

如果你需要在c#端做到这一点,你可以通过子类化DynamicObject和重载Try*Member函数来实现同样的事情。