6

在IronPython中使用 .NET 值类型时,有一个众所周知的问题。最近,当我尝试在 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’

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

4

4 回答 4

4

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

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 代码可以正常设置字段,并且您的代码可以Optimized在需要将数据提供给 OpenGL 或 XNA 或您正在使用的任何东西时调用。

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

class BetterVector {
   // ...
   public static implicit operator Vector(BetterVector v) {
       return v.Optimized;
   }
}
于 2010-04-21T22:43:05.020 回答
2

你打电话时

obj.position.x = 1

你得到的是 obj 对象为你提供了一个 position 结构的实例,它本质上是一个副本,因此,设置 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
于 2010-04-20T19:37:03.593 回答
1

我发现更新结构的唯一方法是利用这样一个事实,即您可以在创建结构时指定任何公共字段/属性。语法看起来像 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' 这样的语法来创建新结构,从旧结构复制字段,那就太好了。不幸的是,我们必须在克隆结构时指定所有字段。

于 2010-04-23T07:34:11.513 回答
-1

令人费解的是,您最终陷入了这样的困境。在 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 中。

于 2010-06-15T04:18:40.760 回答