【问题标题】:TVector3D.AddVector3D does not work (for me)TVector3D.AddVector3D 不起作用(对我来说)
【发布时间】:2013-07-24 20:25:03
【问题描述】:

使用 Delphi XE4 (update1) 和 FMX,我想使用以下代码移动相机(在滚轮鼠标事件中):

AVector := Vector3D(0, 0, 3);
Camera.Position.Vector.AddVector3D(AVector);

代码编译,什么都不做。使用调试器时,Camera.Position.Vector不会改变

换行时
AVector := Vector3D(0, 0, 3);
Camera.Position.Vector := Camera.Position.Vector + AVector;

...它有效!很奇怪,AddVector3D() 函数正是执行此代码!!!

procedure TVector3D.AddVector3D(const AVector3D: TVector3D);
begin
  Self := Self + AVector3D;
end;

我的代码有什么问题???

【问题讨论】:

    标签: delphi firemonkey delphi-xe4


    【解决方案1】:

    Camera.Position 的类型为 TPosition3D。而TPosition3D.VectorTVector3D 类型的属性,带有一个getter 和一个setter。注意TVector3D是一条记录,它是值类型而不是引用类型。这个细节至关重要。

    所以,当您写 Camera.Position.Vector 时,您指的是向量的副本。这是一个副本,因为TVector3D 是一个值类型。

    所以,非工作代码相当于:

    var
      TempVec: TVector3D;
    ....
    TempVec := Camera.Position.Vector;
    TempVec.AddVector3D(...);
    

    显然,对AddVector3D 的调用不会修改Camera.Position,因为对AddVector3D 的调用只会改变临时本地。

    在您的代码中,该临时局部变量仍然存在,但您没有给它命名。这是一个隐藏的隐式变量。

    为了修改位置,您必须对Vector 属性进行赋值,这正是工作代码所做的。

    作为一般规则,支持就地突变的值类型通常表示设计不佳。所以,在我看来,Embarcadero 工程师不应该添加像AddVector3DNormalizeScale 等变异实例方法。这种方法会导致这种混乱。相反,使用返回新值的函数可以更好地实现该功能,就像重载运算符所做的那样。

    【讨论】:

    • @TridenT 这有意义吗?
    • 看到Camera.Position.Vector 返回了向量的副本,我真的很惊讶(也很伤心)......大卫一如既往地回答了你的答案:)谢谢细节!
    • 返回副本就好了。它在概念上与Integer 类型的属性没有什么不同,比如说,它也返回一个副本,因为Integer 是一个值类型。不同之处在于TVector3D 具有修改Self 的实例方法。在我看来,这些变异实例方法的存在导致了混乱。
    猜你喜欢
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-03
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    相关资源
    最近更新 更多