C# - 如何修改 getter 给出的结构?

C# - How can I modify a struct given by a getter?

我是 C# 的新手,我还在摸索。 我的问题是我有这样的 class:

class Sprite {
    private Vector3 _position;

    public Vector3 Position {
        get { return _position; }
        set { _position = value;
              HandleEvent(); }
    }

    public Sprite() {
        _position = new Vector3();
    }
}

使用 Vector3 对象和 get/set 块。 这是 Vector3 结构的简化版本:

struct Vector3 {
    float X, Y, Z;
}

现在我的问题是,如果我这样做:

Sprite sprite = new Sprite();
sprite.Position.X += 4.0F;

它给我一个错误,因为 Position 不是一个变量,而是 getter 给我的。但是我不想做_position public,因为我想在变量改变时处理一个事件。

(在 Java 中这非常有效,这就是我感到困惑的原因:/)

你能告诉我我做错了什么或者你会做些什么吗?

编辑:

是的,Vector3 是一个结构而不是 class。抱歉造成混淆,但我是 C# 的新手。

应该工作正常。本示例编译:

namespace ConsoleApplication8
{
    class Sprite
    {
        private Vector3 _position;

        public Vector3 Position
        {
            get { return _position; }
            set
            {
                _position = value;
                // HandleEvent();
            }
        }

        public Sprite()
        {
            _position = new Vector3();
        }
    }

    class Vector3
    {
        public float X, Y, Z;
    }

    class Program
    {
        static void Main()
        {
            Sprite sprite = new Sprite();

            sprite.Position.X += 4.0F;
        }
    }
}

因为你的向量似乎是一个结构,因为结构是一个值类型(这意味着你的 getter 被调用将 return a copy 你的位置)你不能对你的 getter return 进行操作。您将需要创建一个新的 Vector 并将其作为一个整体进行分配。请注意,这似乎是 优化 某人为您制作的。如果 Vector 对象定义属于您,只需将其设为 class,这样它的工作方式就像您习惯于 Java.

如果您希望这是一个结构,您将必须创建一个新结构并分配给它:

            sprite.Position = new Vector 
                              {
                                  X = sprite.Position.X + 4.0F,
                                  Y = sprite.Position.Y,
                                  Z = sprite.Position.Z
                              };

试试这个:

 Sprite sprite = new Sprite();
        Vector3 v = sprite.Position;
        v.X += 0.4F;
        sprite.Position = v;

错误原因:

问题是 sprite.Position returns 一个结构,因此当您访问它时,由于值语义,它会创建它的副本。 += 尝试修改该值,但它会修改一个临时副本 - 而不是存储在 sprite.

中的实际结构

您必须从读取 Position 中获取值并将其放入局部变量,更改该局部变量,然后将局部变量赋值回 Position。

(In Java this works perfectly, that's why I'm confused :/)

Java 没有 C# 调用的 struct.

在 C# 中,struct 创建一个 value type。当你 return 来自 属性 getter 的值类型时,你不会 return 对某处已经存在的对象的引用,你 return 一个全新的值的副本。因此,修改该值的副本没有意义:没有人会看到修改。

在 Java 中,您将为此使用 class。在 C# 中,您可以执行相同的操作。这创建了一个引用类型。当您 return 来自 属性 getter 的引用类型时,您不会 return 对象所有字段的副本,您只是 return 一个引用到同一个对象。在这里,修改确实有意义。您对同一个对象有多个引用,并且通过任何其他引用仍然可以看到修改。

因为 Vector3 是一个结构,它会被隐式地复制。例如,以下代码创建 两个 个向量,而不是一个:

Vector3 tmp1 = new Vector3();
Vector3 tmp2 = tmp1;

如果您最习惯于 类,这可能会造成混淆,其中除非您明确说明(例如使用 new,否则不会复制和创建实例)。

将以上内容与 C# 属性是方法的语法糖这一事实结合起来,您就会得到答案。这个:

Sprite sprite = new Sprite();
sprite.Position.X += 4.0F;

是这样看的:

Sprite sprite = new Sprite();
sprite.get_Position().X += 4.0F;

会这样执行:

Sprite sprite = new Sprite();
Vector3 tmp1 = sprite.get_Position();
tmp1.X += 4.0F;

请注意 tmp1 是全新的 Vector3。它在各个方面都与 sprite.Position 无关,除了它们在某些点包含相同的坐标。

更改临时 Vector3 不会影响 属性 返回的 Vector3。 C# 编译器知道这个不幸的模式并拒绝编译它,因为它几乎肯定不是你想要的。

要解决此问题,您可以尝试像上面那样显式地占据位置,然后显式地放回去:

Sprite sprite = new Sprite();
Vector3 tmp1 = sprite.Position;
tmp1.X += 4.0F;
sprite.Position = tmp1;

一般来说,出于这样的原因,您应该避免使用可变结构。


Java 没有这个问题,因为它根本没有结构。