Почему Python.NET использует базовый метод вместо метода производного класса?

Изменить: это будет исправлено в новой версии pythonnet (когда это запрос на вытягивание объединен).

У меня проблема с наследованием Python.NET. У меня есть DLL, которая состоит из следующего кода:

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

Я ожидаю, что вызов метода Transmit экземпляра InheritedClass запишет в консоль и вернет true, а метод Transmit экземпляра BaseClass вызовет исключение NotImplementedException.

При запуске следующего кода Python:

## 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

Я использую pythonnet версии 2.3.0 и .NET Framework 4.6.1. Спасибо за вашу помощь!

Изменить: на этот вопрос не отвечает этот вопрос. Там сказано, что

Модификатор new указывает компилятору использовать реализацию вашего дочернего класса вместо реализации родительского класса. Любой код, который не ссылается на ваш класс, но на родительский класс, будет использовать реализацию родительского класса.

что явно не то, что здесь происходит.

Редактировать 2: похоже, это проблема с библиотекой pythonnet. Ошибка теперь находится на github.


person Benjamin Minixhofer    schedule 18.10.2018    source источник
comment
Вы используете pythonnet.github.io?   -  person mjwills    schedule 18.10.2018
comment
Да. Этот пакет PyPi.   -  person Benjamin Minixhofer    schedule 18.10.2018
comment
@BenjaminMinixhofer это действительный отчет об ошибке даже в основной ветке, пожалуйста, отправьте его на github, и вклад приветствуется!   -  person denfromufa    schedule 20.10.2018
comment
получи мои 200 баллов, если исправишь эту проблему в pythonnet!   -  person denfromufa    schedule 21.10.2018
comment
@BenjaminMinixhofer Но возникает ли та же проблема, когда вы используете подход виртуального переопределения?   -  person Nkosi    schedule 21.10.2018


Ответы (1)


Разрешение метода pythonnet (метод Bind, найденный в файле methodbinder.cs) можно значительно улучшить, ИМХО. В любом случае, этот метод в настоящее время не заботится об иерархии типов.

Одним из простых решений является изменение класса MethodSorter таким образом, чтобы он отдавал предпочтение производному классу при сортировке методов, чтобы определить, какой из них будет выбран вызовом Bind.

До:

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

После:

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

Примечание. Я не проводил обширных тестов, поэтому я не уверен, что это не сломает что-то еще. Как я уже сказал, код привязки всего этого метода кажется хрупким.

person Simon Mourier    schedule 21.10.2018
comment
не могли бы вы отправить PR с этим предлагаемым исправлением в pythonnet? если это пройдет существующие тесты в CI, то это должно превратиться в pythonnet. - person denfromufa; 22.10.2018
comment
@denfromufa - я сделал это, но github, похоже, в настоящее время имеет странные проблемы с производительностью/кэшем/какими бы то ни было проблемами... - person Simon Mourier; 22.10.2018