- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
(这不是 What is the difference between a field and a property in C# 的副本 - 而是一个更微妙的问题)
我有一个不可变类 Camera(来自 this 光线追踪示例),它工作正常:
public class Camera
{
public Vector3D Pos;
public Vector3D Forward;
public Vector3D Up;
public Vector3D Right;
public Camera(Vector3D pos, Vector3D lookAt)
{
Pos = pos;
Forward = lookAt - pos;
Forward.Normalize();
Vector3D Down = new Vector3D(0, -1, 0);
Right = Vector3D.CrossProduct(Forward, Down);
Right.Normalize();
Right *= 1.5;
Up = Vector3D.CrossProduct(Forward, Right);
Up.Normalize();
Up *= 1.5;
}
}
由于这是一个不可变的类,我想将字段更改为只读属性,我在整个应用程序中都没有遇到任何问题,因此:
public Vector3D Pos { get; private set; }
public Vector3D Forward { get; private set; }
public Vector3D Up { get; private set; }
public Vector3D Right { get; private set; }
其余代码未更改 - 但应用程序不再正常运行。经调查,Forward、Up 和 Right 的值不再标准化。 Normalize,在构造函数(上面)中调用了 3 次,是一个 void 方法。似乎在属性上而不是在字段上调用这样的方法(至少在构造函数中)不会导致属性的值被更新。看起来好像向量(一个结构)是通过引用而不是通过值传递的,如果这有意义的话。
我能找到让它工作的唯一方法是在设置属性值之前在构造函数中完成我的所有转换,如下所示:
public Camera(Vector3D pos, Vector3D lookAt)
{
var forward = lookAt - pos;
forward.Normalize();
var down = new Vector3D(0, -1, 0);
var right = Vector3D.CrossProduct(forward, down);
right.Normalize();
right *= 1.5;
var up = Vector3D.CrossProduct(forward, right);
up.Normalize();
up *= 1.5;
Pos = pos;
Forward = forward;
Up = up;
Right = right;
}
谁能给我解释一下这里到底发生了什么?我知道属性和字段之间存在差异如果您在 get 或 set 中添加任何额外的行为,但我不是。
最佳答案
这是可变值类型的众多问题之一,也是应该完全避免它们的原因。
在您的第一个场景中:
public Vector3D Forward;
Forward.Normalize();
存储在变量 Forward
中的 value 是 Vector3D
本身的实例。因此,当您调用 Forward()
时,您正在改变存储在 Forward
中的值,一切都按预期进行。
但是,在下面的代码中:
private Vector3D forward;
public Vector3D Forward
{
get { return forward; }
private set { forward = value; }
}
Forward.Normalize();
Normalize
是在forward
字段中存储的值的副本 上调用的(请记住,C# 中的默认行为是传递参数按值(value))。因为值类型的变量值本身就是值类型实例,所以您所做的是改变一个副本,而不是存储在forward
中的值。
在您的情况下,该属性是自动生成的,但原理是相同的。自动生成的属性也有一个支持字段,编译器只是为您省去所有涉及的管道的麻烦。
关于c# - C#中字段和属性之间的细微差别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42221783/
你能比较一下属性吗 我想禁用文本框“txtName”。有两种方式 使用javascript,txtName.disabled = true 使用 ASP.NET, 哪种方法更好,为什么? 最佳答案 我
Count 属性 返回一个集合或 Dictionary 对象包含的项目数。只读。 object.Count object 可以是“应用于”列表中列出的任何集合或对
CompareMode 属性 设置并返回在 Dictionary 对象中比较字符串关键字的比较模式。 object.CompareMode[ = compare] 参数
Column 属性 只读属性,返回 TextStream 文件中当前字符位置的列号。 object.Column object 通常是 TextStream 对象的名称。
AvailableSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。 object.AvailableSpace object 应为 Drive 
Attributes 属性 设置或返回文件或文件夹的属性。可读写或只读(与属性有关)。 object.Attributes [= newattributes] 参数 object
AtEndOfStream 属性 如果文件指针位于 TextStream 文件末,则返回 True;否则如果不为只读则返回 False。 object.A
AtEndOfLine 属性 TextStream 文件中,如果文件指针指向行末标记,就返回 True;否则如果不是只读则返回 False。 object.AtEn
RootFolder 属性 返回一个 Folder 对象,表示指定驱动器的根文件夹。只读。 object.RootFolder object 应为 Dr
Path 属性 返回指定文件、文件夹或驱动器的路径。 object.Path object 应为 File、Folder 或 Drive 对象的名称。 说明 对于驱动器,路径不包含根目录。
ParentFolder 属性 返回指定文件或文件夹的父文件夹。只读。 object.ParentFolder object 应为 File 或 Folder 对象的名称。 说明 以下代码
Name 属性 设置或返回指定的文件或文件夹的名称。可读写。 object.Name [= newname] 参数 object 必选项。应为 File 或&
Line 属性 只读属性,返回 TextStream 文件中的当前行号。 object.Line object 通常是 TextStream 对象的名称。 说明 文件刚
Key 属性 在 Dictionary 对象中设置 key。 object.Key(key) = newkey 参数 object 必选项。通常是 Dictionary 
Item 属性 设置或返回 Dictionary 对象中指定的 key 对应的 item,或返回集合中基于指定的 key 的&
IsRootFolder 属性 如果指定的文件夹是根文件夹,返回 True;否则返回 False。 object.IsRootFolder object 应为&n
IsReady 属性 如果指定的驱动器就绪,返回 True;否则返回 False。 object.IsReady object 应为 Drive&nbs
FreeSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。只读。 object.FreeSpace object 应为 Drive 对象的名称。
FileSystem 属性 返回指定的驱动器使用的文件系统的类型。 object.FileSystem object 应为 Drive 对象的名称。 说明 可
Files 属性 返回由指定文件夹中所有 File 对象(包括隐藏文件和系统文件)组成的 Files 集合。 object.Files object&n
我是一名优秀的程序员,十分优秀!