views:

93

answers:

3

I want to create an instance of an IronPython class from C#, but my current attempts all seem to have failed.

This is my current code:

ConstructorInfo[] ci = type.GetConstructors();

foreach (ConstructorInfo t in from t in ci
                              where t.GetParameters().Length == 1
                              select t)
{
    PythonType pytype = DynamicHelpers.GetPythonTypeFromType(type);
    object[] consparams = new object[1];
    consparams[0] = pytype;
    _objects[type] = t.Invoke(consparams);
    pytype.__init__(_objects[type]);
    break;
}

I am able to get the created instance of the object from calling t.Invoke(consparams), but the __init__ method doesn't seem to be called, and thus all the properties that I set from my Python script aren't used. Even with the explicit pytype.__init__ call, the constructed object still doesn't seem to be initialised.

Using ScriptEngine.Operations.CreateInstance doesn't seem to work, either.

I'm using .NET 4.0 with IronPython 2.6 for .NET 4.0.

EDIT: Small clarification on how I'm intending to do this:

In C#, I have a class as follows:

public static class Foo
{
    public static object Instantiate(Type type)
    {
        // do the instantiation here
    }
}

And in Python, the following code:

class MyClass(object):
    def __init__(self):
        print "this should be called"

Foo.Instantiate(MyClass)

The __init__ method never seems to be called.

+1  A: 

Looks like you're looking for the answer given to this SO question.

Alex Martelli
The answers never actually said anything about instantiating the object -- the Calculator class in the example has no constructor method and thus it doesn't really answer my question.
rfw
That SO Q's solution works fine for me, and adding an `__init__` method does get it executed (using the `.Operations` instance as shown there). I don't have a .NET 4.0 to try, but I'd be astonished if they broke things in the 4.0 transition.
Alex Martelli
That example isn't entirely suited for me, since I'm passing the type itself to C# code, rather than a scoped variable, i.e. via a function, which might be why the `__init__` method isn't called.
rfw
+3  A: 

This code works with IronPython 2.6.1

    static void Main(string[] args)
    {
        const string script = @"
class A(object) :
    def __init__(self) :
        self.a = 100

class B(object) : 
    def __init__(self, a, v) : 
        self.a = a
        self.v = v
    def run(self) :
        return self.a.a + self.v
";

        var engine = Python.CreateEngine();
        var scope = engine.CreateScope();
        engine.Execute(script, scope);

        var typeA = scope.GetVariable("A");
        var typeB = scope.GetVariable("B");
        var a = engine.Operations.CreateInstance(typeA); 
        var b = engine.Operations.CreateInstance(typeB, a, 20);
        Console.WriteLine(b.run()); // 120
    }

EDITED according to clarified question

    class Program
    {
        static void Main(string[] args)
        {
            var engine = Python.CreateEngine();
            var scriptScope = engine.CreateScope();

            var foo = new Foo(engine);

            scriptScope.SetVariable("Foo", foo);
            const string script = @"
class MyClass(object):
    def __init__(self):
        print ""this should be called""

Foo.Create(MyClass)
";
            var v = engine.Execute(script, scriptScope);
        }
    }

public  class Foo
{
    private readonly ScriptEngine engine;

    public Foo(ScriptEngine engine)
    {
        this.engine = engine;
    }

    public  object Create(object t)
    {
        return engine.Operations.CreateInstance(t);
    }
}
desco
The thing is that I'm not using `scope.GetVariable` -- I'm passing the types directly to C# in the form of the C# `Type` class, mainly because I need to call the function from all sorts of places and the variable might not necessarily be available in that scope.
rfw
Even though I answered my own question, I still think you deserve to be the accepted answer :)
rfw
+2  A: 

I think I solved my own question -- using the .NET Type class seems to have discarded Python type information.

Replacing it with IronPython.Runtime.Types.PythonType works quite well.

rfw