【问题标题】:How to handle value types when embedding IronPython in C#?在 C# 中嵌入 IronPython 时如何处理值类型?
【发布时间】:2010-04-18 23:12:02
【问题描述】:

IronPython 中使用 .NET 值类型时,有一个 well known issue。最近,当我尝试在 C# 中使用 Python 作为嵌入式脚本语言时,这让我很头疼。问题可以总结如下:

给定一个 C# 结构,例如:

struct Vector {
    public float x;
    public float y;
}

还有一个 C# 类,例如:

class Object {
    public Vector position;
}

IronPython 中会发生以下情况:

obj = Object()
print obj.position.x    # prints ‘0’
obj.position.x = 1
print obj.position.x    # still prints ‘0’

正如文章所述,这意味着值类型大多是不可变的。但是,这是一个问题,因为我正计划使用如上所示实现的向量库。是否有任何解决方法可以使用依赖于值类型的现有库?修改库将是最后的手段,但我宁愿避免这样做。

【问题讨论】:

  • 这行得通吗? obj = Object() pos = Vector() pos.x = 1 obj.position = pos print obj.position.x
  • 是的,这行得通。通过适当的构造函数,也可以使用obj.position = Vector(1,0)。这可能是可以接受的,即使我在问题中描述的行为仍然令人困惑......

标签: c# .net ironpython embedding value-type


【解决方案1】:

无需修改库,使用代理即可。

struct Vector {
    public float X;
    public float Y;
}

class BetterVector {
    public float X;
    public float Y;
    public Vector Optimized { get { return new Vector(X, Y); } }
}

class Object {
    public BetterVector Position { get; set; }
}

现在 Python 代码可以正常设置字段,并且当您的代码需要将数据提供给 OpenGL 或 XNA 或您正在使用的任何东西时,您的代码可以调用 Optimized

如果调用 Optimized 看起来工作量太大,您甚至可以使用隐式强制:

class BetterVector {
   // ...
   public static implicit operator Vector(BetterVector v) {
       return v.Optimized;
   }
}

【讨论】:

  • 这很酷,尤其是隐式强制。一个人必须编写一堆代理,但这不应该是太多的工作。我会把这个问题留得更久一些,看看是否还有其他建议,但我喜欢这种方法。
【解决方案2】:

当你打电话时

obj.position.x = 1

你得到的是 obj 对象为你提供了一个位置结构的实例,它本质上是一个副本,因此设置 X 值不会被传播。

你说的是 obj.position = Vector(1,0) 是你应该做的。类似的事情发生在 C# 中。


编辑 - 可能的解决方法。

如果您不想设置构造函数,我相信这会起作用:

obj = Object()
pos = obj.position; # gets the object
pos.x = 1
pos.y = 2
obj.position = pos # I'm not sure if this line is necessary

【讨论】:

  • 嗯,是的,也不是。我了解值类型的概念,并且可以看到在将它们转换为 IronPython 时它们存在的困难。但是,当前的解决方案不同于 C# 中的行为(如链接文章中所述),也不同于 Python 的工作方式(没有值类型)。如果 IronPython 有类似 getattrref 的函数,那将使用户能够重新创建 C# 行为,据我所知,目前这是不可能的。
  • 是的,C# 不同,但行为有点相似。我已经更新了我的回复,这可能会更好一些
  • 这不是让它在语法上看起来相似的好方法,但我想知道是否有办法获得与 C# 中相同的语义(即不创建 Vector 的新副本)。
  • 我不知道有什么方法可以在 IronPython 中获得相同的语义,但我在 IronPython 中仍然非常生疏。您在 IronRuby 中尝试过吗?它可能会满足您项目的要求并提供不同的行为。
  • 该变通方法也不起作用。它有同样的复制问题。 >>> bar = MyStruct() >>> print "%d:%d" % (bar.x, bar.y) 0:0 >>> bar.x = 8 >>> print "%d:%d " % (bar.x, bar.y) 0:0
【解决方案3】:

我发现更新结构的唯一方法是利用这样一个事实,即您可以在创建结构时指定任何公共字段/属性。语法看起来像 Python 中的命名/可选参数。

namespace StructExample
{
    public struct MyStruct
    {
        public int x;
        public int y { get; set; }
    }

    public class MyClass
    {
        public MyStruct a;
    }
}

我们可以像这样使用 IronPython 中的类:

>>> foo = MyClass()
>>> print "%d:%d" % (foo.a.x, foo.a.y)
0:0
>>> foo.a.x = 1 # doesn't work!
>>> print "%d:%d" % (foo.a.x, foo.a.y)
0:0
>>> foo.a = MyStruct(x=1,y=2)
>>> print "%d:%d" % (foo.a.x, foo.a.y)
1:2

如果 Python 有像 F# 'with' 这样的语法来创建新结构,从旧结构复制字段,那就太好了。不幸的是,我们必须在克隆结构时指定所有字段。

【讨论】:

  • 感谢您提到 kwargs 构造函数,这非常方便。
【解决方案4】:

很令人费解的是,你最终陷入了这样的困境。在 python 中(在 IronPython 2.6.1、.Net 4.0 上试过)等效代码是这样的:

>>> class a:
...  x = 0
...  y = 0
...
>>> class b:
...  Vector = a()
...
>>> c = b()
>>> c.Vector.x = 1
>>> print c.Vector.x
1

请注意,我的伪代码和您的伪代码之间有一个区别——静态属性被分配了一个类的实例,而不仅仅是类型定义。作为副作用,当 b 被实例化时,一个类的实际实例被初始化为 b.Vector。

(伪代码仍然“损坏” - 初始化必须进入 def init(self),但那是另一回事)

这个例子的寓意是,不要让“公共向量位置”未初始化,而是将“位置”的初始化构建到类 Object 中。

【讨论】:

  • 感谢您的评论。但是,我认为它不适用,因为我在谈论 .NET 值类型。我知道仅使用 IronPython 类不会遇到同样的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多