我正在使用自动实现属性。 我猜修复以下代码最快的方法是声明自己的支持变量?
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
错误信息:无法修改“表达式”的返回值,因为它不是一个变量
试图修改中间表达式的值类型。由于该值没有被持久化,所以该值将不会改变。
要解决此错误,请将表达式的结果存储在中间值中,或使用引用类型作为中间表达式。
我正在使用自动实现属性。 我猜修复以下代码最快的方法是声明自己的支持变量?
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
错误信息:无法修改“表达式”的返回值,因为它不是一个变量
试图修改中间表达式的值类型。由于该值没有被持久化,所以该值将不会改变。
要解决此错误,请将表达式的结果存储在中间值中,或使用引用类型作为中间表达式。
这是因为Point
是一个值类型(struct
)。
由于这一点,当访问Origin
属性时,您访问的是类所持有的值的副本,而不是像引用类型(class
)那样直接访问该值本身。因此,如果您在副本上设置X
属性,那么您就是在副本上设置该属性,然后丢弃它,从而保留原始值不变。这可能不是您想要的,这也是编译器警告您的原因。
如果您只想更改X
值,则需要执行类似以下操作:
Origin = new Point(10, Origin.Y);
使用支撑变量是无用的。 Point
类型是一个值类型。
您需要将整个Point
值分配给Origin
属性:
Origin = new Point(10, Origin.Y);
问题在于当你访问Origin属性时,get
返回的是Origin属性自动创建字段中Point结构的副本。因此,对这个副本的X字段进行修改不会影响底层字段。编译器检测到这一点并给出错误提示,因为这个操作完全没有意义。
即使你使用了自己的后备变量,你的get
看起来也像:
get { return myOrigin; }
你仍然会返回 Point 结构的副本,而且会得到相同的错误。
嗯... 经过仔细阅读您的问题,也许您实际上是想直接从类内部修改后备变量:
myOrigin.X = 10;
是的,那就是你需要的。
X
),则可以使用对象初始化程序(它将在幕后执行所有魔术)。请注意,您不需要使结构体不可变,但只是提供额外的信息:struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
class MyClass
{
public Point Origin { get; set; }
}
MyClass c = new MyClass();
c.Origin.X = 23; //fails.
//but you could do:
c.Origin = new Point { X = 23, Y = c.Origin.Y }; //though you are invoking default constructor
//instead of
c.Origin = new Point(23, c.Origin.Y); //in case there is no constructor like this.
Point tmp = new Point();
tmp.X = 23;
tmp.Y = Origin.Y;
c.Origin = tmp;
Origin.Y
的值吗?对于类型为 Point
的属性,我认为更符合惯用法的是仅更改 X
,如 var temp=thing.Origin; temp.X = 23; thing.Origin = temp;
。惯用方法的优点在于它不必提及它不想修改的成员,这是因为 Point
是可变的。我对这种哲学感到困惑,因为编译器不能允许 Origin.X = 23;
,所以应该设计一个结构体来要求像 Origin.X = new Point(23, Origin.Y);
这样的代码。后者对我来说似乎真的很棘手。 - supercatX
和 Y
给特定的构造函数)。现在当一个人可以使用 Point p = new Point()
时,它就失去了意义。我知道为什么结构体真正需要它,所以不用考虑这个问题。但是你有没有一个很酷的想法来更新只有一个属性,比如 X
? - nawfalobject o = 5;
,那么 o
将持有一个指向堆对象类型 System.Int32
的实例的引用,该类型派生自 System.Object
。然而,如果有人改为说 int i = 5;
,那么 i
将持有位模式 0000...00000101。如果将值类型视为存在于与堆对象类型不同的自己的“宇宙”中,则它们的行为就会有意义。但是,如果假装它们是 System.Object
的派生类,那么就不会有意义。我建议采用与实际行为相匹配的世界观。 - supercat我认为很多人在这里感到困惑,这个问题特别涉及理解值类型的属性返回值类型的副本(与方法和索引器一样),以及直接访问值类型的字段。以下代码通过直接访问属性的后备字段来实现您试图实现的目标(注意:用后备字段表达属性相当于自动属性,但具有我们可以直接访问后备字段的优势):
class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.SetOrigin();
Debug.Assert(myClass.Origin.X == 10); //succeeds
}
}
class MyClass
{
private Point _origin;
public Point Origin
{
get => _origin;
set => _origin = value;
}
public void SetOrigin()
{
_origin.X = 10; //this works
//Origin.X = 10; // fails with CS1612;
}
}
class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.SetOrigin();
Debug.Assert(myClass.Origin.X == 10); //throws error
}
}
class MyClass
{
private Point _origin;
public Point Origin
{
get => _origin;
set => _origin = value;
}
public void SetOrigin()
{
var origin = Origin;
origin.X = 10; //this is only changing the value of the local copy
}
}
Origin
声明为类的字段而不是属性是否更容易呢? 我想这样做可以实现你的目标。struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
class MyClass
{
public Point Origin;
}
MyClass c = new MyClass();
c.Origin.X = 23; // No error. Sets X just fine
我猜这里的问题在于你试图在语句中分配对象的子值,而不是分配对象本身。在这种情况下,你需要分配整个 Point 对象,因为属性类型是 Point。
Point newOrigin = new Point(10, 10);
Origin = newOrigin;
希望我表达得清楚
Point
是一个可变的类类型,原始代码将会设置由属性Origin
返回的对象中的字段或属性X
。我看不出有什么理由相信这会对包含Origin
属性的对象产生期望的影响。一些框架类具有属性,它们将它们的状态复制到新的可变类实例并返回。这样的设计有一个优点,就是允许像thing1.Origin = thing2.Origin;
这样的代码设置对象原点的状态以匹配另一个对象的状态,但它不能警告像thing1.Origin.X += 4;
这样的代码。 - supercatPoint
是引用类型的成员,那么它将不会在堆栈上,而是在包含对象内存的堆上。 - Greg Beech只需按照以下方式删除“get set”属性,然后一切都像往常一样工作。
如果是原始类型,请改用 get;set;...
using Microsoft.Xna.Framework;
using System;
namespace DL
{
[Serializable()]
public class CameraProperty
{
#region [READONLY PROPERTIES]
public static readonly string CameraPropertyVersion = "v1.00";
#endregion [READONLY PROPERTIES]
/// <summary>
/// CONSTRUCTOR
/// </summary>
public CameraProperty() {
// INIT
Scrolling = 0f;
CameraPos = new Vector2(0f, 0f);
}
#region [PROPERTIES]
/// <summary>
/// Scrolling
/// </summary>
public float Scrolling { get; set; }
/// <summary>
/// Position of the camera
/// </summary>
public Vector2 CameraPos;
// instead of: public Vector2 CameraPos { get; set; }
#endregion [PROPERTIES]
}
}