3

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 );

1 Answer 1

3

Short answer: nope, you can't modify a .NET class. By default, they don't have the necessary dynamic hooks to add members.

Your best bet is to wrap the class and add the members you want; Python makes this easy:

class FooWrapper(object):
  def __init__(self, foo):
    self.foo = foo

  def __getattr__(self, name):
    return getattr(self.foo, name)

  def mymethod(self):
    ...

The __getattr__ special method is only called for attributes that are not part of normal attribute lookup, so mymethod() will be looked up normally but anything else will be forwarded to the underlying object.

If you need to do it on the C# side, you can achieve the same thing by subclassing DynamicObject and overloading the Try*Member functions.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 tried both ways and they work, though it's quite some work to make it generic and useful in production I guess

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.