Реализовать интерфейс C# в Python для.NET
Мне дали библиотеку, написанную на C#, которую я пытаюсь вызвать, используя Python для.NET.
Основной класс, в котором мне нужен экземпляр, имеет конструктор, такой как:
GDhuClient(IGDhuSettings)
Нет (выставленных) классов, которые реализуют IGDhuSettings
интерфейс. Когда я создаю класс Python для его реализации, например,
class PyGDhuSettings(IGDhuSettings):
...
я получил TypeError: interface takes exactly one argument
если у меня нет __new__
метод или, если я определю один нормальный способ:
def __new__(cls):
return super().__new__(cls)
Если я пытаюсь создать экземпляр интерфейса, как если бы это был класс, я либо получаю ту же ошибку (без аргументов или>1), либо <whatever> does not implement IGDhuSettings
если я передам это единственный аргумент.
Глядя на источник Python для.NET,
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Python.Runtime
{
/// <summary>
/// Provides the implementation for reflected interface types. Managed
/// interfaces are represented in Python by actual Python type objects.
/// Each of those type objects is associated with an instance of this
/// class, which provides the implementation for the Python type.
/// </summary>
internal class InterfaceObject : ClassBase
{
internal ConstructorInfo ctor;
internal InterfaceObject(Type tp) : base(tp)
{
var coclass = (CoClassAttribute)Attribute.GetCustomAttribute(tp, cc_attr);
if (coclass != null)
{
ctor = coclass.CoClass.GetConstructor(Type.EmptyTypes);
}
}
private static Type cc_attr;
static InterfaceObject()
{
cc_attr = typeof(CoClassAttribute);
}
/// <summary>
/// Implements __new__ for reflected interface types.
/// </summary>
public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
{
var self = (InterfaceObject)GetManagedObject(tp);
int nargs = Runtime.PyTuple_Size(args);
Type type = self.type;
object obj;
if (nargs == 1)
{
IntPtr inst = Runtime.PyTuple_GetItem(args, 0);
var co = GetManagedObject(inst) as CLRObject;
if (co == null || !type.IsInstanceOfType(co.inst))
{
Exceptions.SetError(Exceptions.TypeError, $"object does not implement {type.Name}");
return IntPtr.Zero;
}
obj = co.inst;
}
else if (nargs == 0 && self.ctor != null)
{
obj = self.ctor.Invoke(null);
if (obj == null || !type.IsInstanceOfType(obj))
{
Exceptions.SetError(Exceptions.TypeError, "CoClass default constructor failed");
return IntPtr.Zero;
}
}
else
{
Exceptions.SetError(Exceptions.TypeError, "interface takes exactly one argument");
return IntPtr.Zero;
}
return CLRObject.GetInstHandle(obj, self.pyHandle);
}
}
}
Я не вижу средства реализации интерфейса C# в Python без CoClass (не определено) или без класса, который его реализует.
Есть ли какой-то нюанс, который я здесь упускаю, или это ограничение Python для.NET?
Обсуждение на GitHub: https://github.com/pythonnet/pythonnet/issues/674
1 ответ
Я обнаружил, что мне нужно добавить поле пространства имен в класс Python, реализующий интерфейс. Насколько я понимаю, именно это поле представляет пространство имен.NET для вашего класса Python, который реализует интерфейс.
В качестве примера. Я сделал библиотеку C# со следующим интерфейсом:
public interface ITestInterface
{
string StringAdd(string text, int number);
}
и тривиальная статическая функция, которая потребляет интерфейс:
public static string InvokeStringAdd(ITestInterface testInterface, string text)
{
return "*** " + testInterface.StringAdd(text, 7) + " ***";
}
Затем на стороне Python я определяю следующий класс, реализующий интерфейс:
class TestInterfaceSubClass(ITestInterface):
__namespace__ = "MyNameSpace"
def StringAdd(self,text,x):
return 'My text <{}> with number <{}>'.format(text, x)
Обратите внимание, что я добавил поле пространства имен и присвоил ему значение "MyNameSpace". Я думаю, что любое не конфликтующее имя пространства имен.NET подойдет.
Чтобы проверить реализацию в Python
test_interface_subclass = TestInterfaceSubClass()
print(TestClass.InvokeStringAdd(test_interface_subclass,'My Text'))
который возвращается
*** My text <My Text> with number <7> ***
В заключение отметим, что классу Python TestInterfaceSubClass предоставляется пространство имен "MyNameSpace". Это означает, что переоценка кода определения класса, например, в блокноте Jupyter, приведет к конфликту пространства имен на стороне.NET, поэтому вам нужно либо присвоить ему другое значение пространства имен, либо перезапустить ядро.