I have some simple C# types that are used in an embedded IronPython script:
//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 );
I'd like to add some functionality to Foo but I cannot modify it's code. Also I'd rather not derive from it nor use extension methods, but use delegates instead. Ideally I'd like to write something like
scope.AddMemberFunction( scope.foo, "Fun",
new Func<Foo,int>( f => return f.a + f.b ) );
and then use it in the Python script directly:
print( foo.Fun() )
I thought this was exactly what could be done using the Operations but this raises an exception saying 'Foo' object has no attribute 'Fun'
engine.Operations.SetMember( scope.foo, "Fun",
new Func<Foo, int>( f => f.a + f.b ) );
Next I tried the Python way of things (say Foo is in a namespace IronPythonApp that is in and it's assembly is added to the engine.Runtime):
import IronPythonApp
def Fun( self ):
return self.a + self.b
IronPythonApp.Foo.Fun = Fun
but this gives a similar exception: can't set attributes of built-in/extension type 'Foo'
Is there a way to modify the Python class definition that ironPython internally generates?
Update
I explored some of the options as by Jeff Hardy's answer. Here are two ways that can both be made pretty scalable (just showing the straightforward way here)
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.
Using SetMember after all
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 );