【问题标题】:Implement a C# Interface in Python for .NET在 Python 中为 .NET 实现 C# 接口
【发布时间】:2018-04-09 15:27:47
【问题描述】:

我得到了一个用 C# 编写的库,我正在尝试使用 Python for .NET 调用它。

我需要一个实例的主类有一个构造函数,例如:

GDhuClient(IGDhuSettings)

没有(公开的)类实现IGDhuSettings 接口。当我创建一个 Python 类来实现它时,例如,

class PyGDhuSettings(IGDhuSettings):
    ...

如果我没有__new__ 方法或者如果我以正常方式定义一个方法,我会得到TypeError: interface takes exactly one argument:

def __new__(cls):
    return super().__new__(cls)

如果我尝试将接口实例化为一个类,我会得到相同的错误(没有或 >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);
        }
    }
}

如果没有 CoClass(没有定义)或已经有一个实现它的类,我看不到一种在 Python 中实现 C# 接口的方法。

我在这里遗漏了一些细微差别,还是这是 Python for .NET 的限制?

在 GitHub 上讨论:https://github.com/pythonnet/pythonnet/issues/674

【问题讨论】:

  • 我没有将 Python for .NET 用于任何严重的事情,但我可以看出这可能是一个故意的限制。您的 Python 代码不在 .NET 中运行,因此它必须为接口的每个方法构建一个代理方法,这可能很慢而且很脆弱,所以他们可能希望您修改您的设计,这样您就可以通过一个委托,还是改用 IronPython?

标签: c# python interface python.net pythonnet


【解决方案1】:

我发现我需要在实现接口的 Python 类中添加 namespace 字段。据我了解,这个字段代表实现接口的 Python 类的 .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 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'))

返回

*** My text <My Text> with number <7> ***

最后请注意,python 类 TestInterfaceSubClass 被赋予命名空间“MyNameSpace”。这意味着重新评估例如 Jupyter 笔记本中的类定义代码将导致 .NET 端的命名空间冲突,因此您必须为其提供不同的命名空间值或重新启动内核。

【讨论】:

  • 我几乎要在我的 dll 上创建事件和诸如此类的东西,这样我就可以在 python 上使用实现了!救了我的命:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 2012-01-22
  • 2012-02-01
  • 2011-07-03
  • 2018-10-11
相关资源
最近更新 更多