【问题标题】:C# - How can I modify a struct given by a getter?C# - 如何修改 getter 给出的结构?
【发布时间】:2016-06-19 01:54:30
【问题描述】:

我对 C# 还很陌生,但我仍在搞清楚。 我的问题是我有这样的课程:

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,因为我想在变量更改时处理一个事件。

(在 Java 中这很完美,这就是我感到困惑的原因:/)

你能告诉我我做错了什么或者你会做些什么不同的事情吗?

编辑:

是的,Vector3 是一个结构而不是一个类。很抱歉造成混乱,但我对 C# 还是很陌生。

【问题讨论】:

  • 你能把错误信息贴出来吗?这应该有效。
  • 你确定 Vector3 是一个类而不是一个结构吗?
  • 在发布的代码中看不到任何错误。一定还有其他内容您没有发布。
  • 你能给我们看看 Vector3 类的代码吗? Vector3 类通常设计为具有只读属性的不可变类型,以实现并行性/线程安全。我怀疑问题出在 Position 属性上,因为这不是您要设置的。
  • 你需要这样做:var temp = sprite.Position; temp.X = 4.0F; sprite.Position = temp;

标签: c# getter


【解决方案1】:

(在 Java 中这很完美,这就是我感到困惑的原因:/)

Java 没有 C# 调用的 struct

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

在 Java 中,您可以为此使用 class。在 C# 中,您也可以这样做。这会创建一个引用类型。当您从属性 getter 返回引用类型时,您不会返回对象所有字段的副本,而只是返回对同一对象的引用。在这里,修改确实有意义。您对同一个对象有多个引用,并且通过任何其他引用仍然可以看到修改。

【讨论】:

  • 感谢您的解释。这绝对可以帮助我:)
【解决方案2】:

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

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 被调用将返回您位置的 copy)您无法对您的 getter 进行操作返回。您将需要创建一个新向量并将其作为一个整体进行分配。请注意,这似乎是某人为您制作的优化。如果 Vector 对象定义属于您,只需将其设为一个类,这样它就可以像您在 Java 中使用的那样工作。

如果你想要这是一个结构,你必须创建一个新的并分配它:

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

【讨论】:

  • 感谢您指出这一点,我编辑了问题。我对 C# 很陌生,所以很抱歉造成混乱。
【解决方案3】:

因为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 没有这个问题,因为它根本没有结构。

【讨论】:

    【解决方案4】:

    试试这个:

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

    错误原因:

    问题是sprite.Position 返回一个结构,所以当你访问它时,由于值语义,它会创建它的副本。 += 尝试修改该值,但它将修改一个临时副本 - 而不是存储在您的 sprite 中的实际结构。

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

    【讨论】:

    • 感谢您的示例。
    猜你喜欢
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-19
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多