Почему 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. Спасибо за вашу помощь!

Изменение: на этот вопрос не ответил. Там говорится, что

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

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

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

Ответ 1

pythonnet метода 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 type derives from m1 type, favor m2
            if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType))
                return 1;

            // m1 type derives from m2 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;
    }
}

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