为什么或如何 Vector2/Vector3 结构是不可变的?

Why or How is a Vector2/Vector3-struct immutable?

提问人:Vrt 提问时间:3/26/2021 更新时间:3/27/2021 访问量:1012

问:

对于我的网格系统(2D数组),我正在创建一个-struct。这与 .Index2Vector2

遇到一些问题,我阅读了结构。遇到可变不可变的主题。

长话短说,可变结构很糟糕。所以后来我想知道,我如何让我的结构不可变?这让我想到了这篇文章

如果理解正确,要使结构不可变,您必须确保在创建实例后,无法修改实例的成员。

但是当我看到统一的 -struct 时。创建 后,可以修改其值:Vector2new Vector2(0f, 0f)

Vector2 myVector = new Vector2(1f, 1f);
myVector.x = 2f;

这很好用。

与 -method 相同:.Set

Vector2 myVector = new Vector2(1f, 1f);
myVector.Set(2f, 2f);

还查看了 a 的定义,我没有看到上面链接的帖子中建议的任何解决方案。Vector

这让我想知道几件事?

  1. a 是不可变的结构吗?Vector2
  2. 如果是,它是如何构建的,使其在能够修改成员的同时仍然不可变?
  3. 如果不是,为什么它仍然是一个安全的结构?
c# unity-game-engine 结构 不变性 可变

评论

0赞 juharr 3/26/2021
这是一个关于另一个 Vector 结构体可变的类似问题。stackoverflow.com/questions/8920080/......
0赞 galdin 3/26/2021
为了完整起见,这里是来源:github.com/Unity-Technologies/UnityCsReference/blob/2020.2.0a13/......Vector2
0赞 derHugo 3/27/2021
它不是..但它是一个分配给属性的结构(例如 transform.position 等),然后你不会简单地这样做,因为这不会调用属性的 settertransform.position.x = ...
0赞 Ruzihm 3/27/2021
“安全结构”是什么意思?
0赞 Vrt 3/27/2021
在阅读有关可变结构的信息时,我的印象是它们使用起来不安全,因为它们可能会导致项目出现问题。我得出的结论是 vector2-struct 是一个可变结构,但也是一个默认的统一结构。我无法想象它们会包含一个会导致项目出现问题的结构。也许问题不是很清楚。但我的想法是,即使 Vector2 是一个可变结构,也许在后台它的结构不会导致自定义可变结构会出现的任何问题。如果是这样,我可以将其应用于我的 Index2。

答:

0赞 Marc Gravell 3/26/2021 #1
  1. 不,它是可变的
  2. 事实并非如此
  3. 事实并非如此;可变结构可能导致问题的所有方式都非常适用;所以:小心点

评论

0赞 Ben Voigt 3/26/2021
回复#3:可变结构“不安全”的想法被夸大了。有一些陷阱。与所有潜在的错误来源相比,这些都是非常小的。
0赞 Marc Gravell 3/26/2021
@BenVoigt确实如此,但问题是,在局外人看来,有风险的使用看起来完全合理;如果您知道不该做什么,它们绝对可以“安全”使用
1赞 Neil Gatenby 3/26/2021 #2

答案 1:不,它不是一成不变的;我想你自己证明了这一点

答案 2:很确定你的问题是一个矛盾;不能修改不可变的对象

回答 3:如果你有想要创建的东西,那么不可变的对象是很好的,一次又一次地使用而不改变,然后你就完成了。如果要创建、使用、更改、再次使用、再次更改等,则它们不适用。如果你有一个场景,事情是创建/使用但不做更改/完成的,那么让它不可变是一件好事,因为你知道它没有被在你背后编辑......它可以像代码中的岩石一样被依赖。这不适合您想要/需要改变的东西(例如,滚动球的位置)。因此,Vector2 是最后一种情况的理想选择。这并不是关于安全和不安全,而是关于适当和不适当的

评论

0赞 Marc Gravell 3/26/2021
Vector2不是“对象”(除非你把它装箱),所以“不可变对象”可能是一个错误的比较;在谈论仓位时——我会说,当你改变仓位时,从逻辑上讲,你不会改变仓位的特征,而是:你把整个仓位改成不同的仓位值——仓位值由 x/y/z(或其他什么)组成的事实是无关紧要的;我会反驳说,不可变的值类型非常适合“位置”类型,只是:你会做什么或其他什么obj.Position = obj.Position.Add(3, 0, 42);
0赞 Neil Gatenby 3/30/2021
有趣的东西@MarcGravell。谢谢
1赞 zambari 3/27/2021 #3

它不是一成不变的。大多数人都遇到过无法修改 Vector3 的单个组件的问题,例如

transform.position.x=value;

这是行不通的,但不是出于不变性的原因。这是行不通的,因为transform.position实际上不是一个字段,而是一个属性(getter setter pair)。在 Unity3D 公开的几乎没有任何东西是 setter/getter 的,所以它的定义不是

公共 Vector3 位置;

但是由于三个独立的元素、一个支持字段和一个 parir 方法运行来检索或设置值,所以(我称我的属性 pos not position 不要与实际的 Transform implementatoin wchich 混淆更复杂)。

private Vector3 _pos;
public Vector3 pos { get { return _pos;} set  { _pos=value;}}

现在诀窍来了。属性本质上只是 C# 中的语法糖,因此在后台编译为类似

private Vector3 _pos;
public Vector3 GetPos () 
   { return _pos; } 
public void SetPos(Vector3 value) 
   {  _pos=value;}}

现在,如果您仔细观察诸如

  transform.position.x+=1;  // won't work

它们没有意义,因为你实际上的意思是:

Vector3 temp = transform.GetPos();
temp.x+=1; // Vectors3 mutate all right
transform.SetPos(temp);

因为 .position 是一个属性,并且有一个 backing 方法,所以它必须执行并返回一个完整的结构,然后才能修改和发送新的结构。如果位置是一个字段,这很好,但转换将无法知道它已更改并应用您的更改(如果您设置全局位置,它确实需要做大量的数学运算)