在 Python 中为 .NET 实现 C# 接口
Implement a C# Interface in Python for .NET
我得到了一个用 C# 编写的库,我正尝试使用 Python for .NET 来调用它。
我需要的主要 class 实例具有如下构造函数:
GDhuClient(IGDhuSettings)
没有(公开的)class 实现 IGDhuSettings
接口。当我创建一个 Python class 来实现它时,例如
class PyGDhuSettings(IGDhuSettings):
...
如果我没有 __new__
方法或者如果我以正常方式定义一个方法,我会得到 TypeError: interface takes exactly one argument
:
def __new__(cls):
return super().__new__(cls)
如果我尝试将接口实例化为 class,我要么得到相同的错误(没有或 >1 个参数),要么 <whatever> does not implement IGDhuSettings
如果我向它传递一个参数.
正在查看 Python for .NET source、
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);
}
}
}
我没有看到在 Python 中实现 C# 接口的方法,既没有 CoClass(没有定义),也没有已经有实现它的 class。
我在这里遗漏了一些细微差别,还是这是 Python 对 .NET 的限制?
关于 GitHub 的讨论:https://github.com/pythonnet/pythonnet/issues/674
我发现我需要将 namespace 字段添加到实现接口的 Python class 中。据我了解,此字段代表实现接口的 Python class 的 .NET 命名空间。
举个例子。我制作了一个具有以下接口的 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 实现接口:
class TestInterfaceSubClass(ITestInterface):
__namespace__ = "MyNameSpace"
def StringAdd(self,text,x):
return 'My text <{}> with number <{}>'.format(text, x)
请注意,我添加了 namespace 字段并为其指定了 "MyNameSpace" 值。我认为任何非冲突的 .NET 命名空间名称都可以。
测试 Python
中的实现
test_interface_subclass = TestInterfaceSubClass()
print(TestClass.InvokeStringAdd(test_interface_subclass,'My Text'))
哪个returns
*** My text <My Text> with number <7> ***
最后请注意,python class TestInterfaceSubClass 被赋予命名空间 "MyNameSpace"。这意味着重新计算 Jupyter notebook 中的 class 定义代码将导致 .NET 端的命名空间冲突,因此您必须为其提供不同的命名空间值或重新启动内核。
我得到了一个用 C# 编写的库,我正尝试使用 Python for .NET 来调用它。
我需要的主要 class 实例具有如下构造函数:
GDhuClient(IGDhuSettings)
没有(公开的)class 实现 IGDhuSettings
接口。当我创建一个 Python class 来实现它时,例如
class PyGDhuSettings(IGDhuSettings):
...
如果我没有 __new__
方法或者如果我以正常方式定义一个方法,我会得到 TypeError: interface takes exactly one argument
:
def __new__(cls):
return super().__new__(cls)
如果我尝试将接口实例化为 class,我要么得到相同的错误(没有或 >1 个参数),要么 <whatever> does not implement IGDhuSettings
如果我向它传递一个参数.
正在查看 Python for .NET source、
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);
}
}
}
我没有看到在 Python 中实现 C# 接口的方法,既没有 CoClass(没有定义),也没有已经有实现它的 class。
我在这里遗漏了一些细微差别,还是这是 Python 对 .NET 的限制?
关于 GitHub 的讨论:https://github.com/pythonnet/pythonnet/issues/674
我发现我需要将 namespace 字段添加到实现接口的 Python class 中。据我了解,此字段代表实现接口的 Python class 的 .NET 命名空间。
举个例子。我制作了一个具有以下接口的 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 实现接口:
class TestInterfaceSubClass(ITestInterface):
__namespace__ = "MyNameSpace"
def StringAdd(self,text,x):
return 'My text <{}> with number <{}>'.format(text, x)
请注意,我添加了 namespace 字段并为其指定了 "MyNameSpace" 值。我认为任何非冲突的 .NET 命名空间名称都可以。
测试 Python
中的实现test_interface_subclass = TestInterfaceSubClass()
print(TestClass.InvokeStringAdd(test_interface_subclass,'My Text'))
哪个returns
*** My text <My Text> with number <7> ***
最后请注意,python class TestInterfaceSubClass 被赋予命名空间 "MyNameSpace"。这意味着重新计算 Jupyter notebook 中的 class 定义代码将导致 .NET 端的命名空间冲突,因此您必须为其提供不同的命名空间值或重新启动内核。