Why does Python.NET use the base method instead of the method from a derived class?
Question:
Edit: This will be fixed in a new version of pythonnet (when this pull request is merged).
I have a problem with Python.NET inheritance. I have a DLL which consists of the following code:
using System;
namespace InheritanceTest
{
public class BaseClass
{
public bool Transmit()
{
throw new NotImplementedException();
}
}
public class InheritedClass: BaseClass
{
public new bool Transmit()
{
Console.WriteLine("Success!");
return true;
}
}
}
I would expect a call to the Transmit
method of an instance of InheritedClass
to write to the console and return true and the Transmit
method of BaseClass
to throw a NotImplementedException.
When running the following python code:
## setup
import clr
import os
clr.AddReference(os.getcwd() + '\InheritanceTest.dll')
import InheritanceTest
## method test
base_class = InheritanceTest.BaseClass()
base_class.Transmit() # throws a NotImplementedException as expected
inherited_class = InheritanceTest.InheritedClass()
inherited_class.Transmit() # still throws a NotImplementedException, although it should call InheritedClass.Transmit
I am using pythonnet version 2.3.0 and .NET Framework 4.6.1. Thanks for your help!
Edit: This is not answered by this question. There, it is said that
The new modifier instructs the compiler to use your child class implementation instead of the parent class implementation. Any code that is not referencing your class but the parent class will use the parent class implementation.
which is clearly not what happens here.
Edit 2: This seems to be a problem with the pythonnet library. The issue is now on github.
Answers:
The pythonnet
method resolution (the Bind
method found in the methodbinder.cs
file) could be improved quite a lot IMHO. Anyway, this method currently doesn’t really care about types hierarchies.
One simple solution is to change the MethodSorter
class so it favors derived class when it sorts methods to determine which one will be choosen by the Bind
call.
Before:
internal class MethodSorter : IComparer
{
int IComparer.Compare(object m1, object m2)
{
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
if (p1 < p2)
{
return -1;
}
if (p1 > p2)
{
return 1;
}
return 0;
}
}
After:
internal class MethodSorter : IComparer
{
int IComparer.Compare(object m1, object m2)
{
var me1 = (MethodBase)m1;
var me2 = (MethodBase)m2;
if (me1.DeclaringType != me2.DeclaringType)
{
// m2's type derives from m1's type, favor m2
if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType))
return 1;
// m1's type derives from m2's type, favor m1
if (me2.DeclaringType.IsAssignableFrom(me1.DeclaringType))
return -1;
}
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
if (p1 < p2)
{
return -1;
}
if (p1 > p2)
{
return 1;
}
return 0;
}
}
Note I’ve not ran extensive tests, so I’m not sure this will not break something else. As I say this whole method binding code seems fragile.
Edit: This will be fixed in a new version of pythonnet (when this pull request is merged).
I have a problem with Python.NET inheritance. I have a DLL which consists of the following code:
using System;
namespace InheritanceTest
{
public class BaseClass
{
public bool Transmit()
{
throw new NotImplementedException();
}
}
public class InheritedClass: BaseClass
{
public new bool Transmit()
{
Console.WriteLine("Success!");
return true;
}
}
}
I would expect a call to the Transmit
method of an instance of InheritedClass
to write to the console and return true and the Transmit
method of BaseClass
to throw a NotImplementedException.
When running the following python code:
## setup
import clr
import os
clr.AddReference(os.getcwd() + '\InheritanceTest.dll')
import InheritanceTest
## method test
base_class = InheritanceTest.BaseClass()
base_class.Transmit() # throws a NotImplementedException as expected
inherited_class = InheritanceTest.InheritedClass()
inherited_class.Transmit() # still throws a NotImplementedException, although it should call InheritedClass.Transmit
I am using pythonnet version 2.3.0 and .NET Framework 4.6.1. Thanks for your help!
Edit: This is not answered by this question. There, it is said that
The new modifier instructs the compiler to use your child class implementation instead of the parent class implementation. Any code that is not referencing your class but the parent class will use the parent class implementation.
which is clearly not what happens here.
Edit 2: This seems to be a problem with the pythonnet library. The issue is now on github.
The pythonnet
method resolution (the Bind
method found in the methodbinder.cs
file) could be improved quite a lot IMHO. Anyway, this method currently doesn’t really care about types hierarchies.
One simple solution is to change the MethodSorter
class so it favors derived class when it sorts methods to determine which one will be choosen by the Bind
call.
Before:
internal class MethodSorter : IComparer
{
int IComparer.Compare(object m1, object m2)
{
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
if (p1 < p2)
{
return -1;
}
if (p1 > p2)
{
return 1;
}
return 0;
}
}
After:
internal class MethodSorter : IComparer
{
int IComparer.Compare(object m1, object m2)
{
var me1 = (MethodBase)m1;
var me2 = (MethodBase)m2;
if (me1.DeclaringType != me2.DeclaringType)
{
// m2's type derives from m1's type, favor m2
if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType))
return 1;
// m1's type derives from m2's type, favor m1
if (me2.DeclaringType.IsAssignableFrom(me1.DeclaringType))
return -1;
}
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
if (p1 < p2)
{
return -1;
}
if (p1 > p2)
{
return 1;
}
return 0;
}
}
Note I’ve not ran extensive tests, so I’m not sure this will not break something else. As I say this whole method binding code seems fragile.