错误: “无法修改返回值”c #

我使用的是自动实现的属性。 我想最快的解决跟踪问题的方法就是声明我自己的后备变量?

public Point Origin { get; set; }


Origin.X = 10; // fails with CS1612

错误消息: 无法修改“ expression”的返回值,因为 它不是一个变量

试图修改一个值类型,该值类型是 中间表达式。由于该值没有持久化,因此 将不会改变。

若要解决此错误,请将表达式的结果存储在 中间值,或者对中间值使用引用类型 表情。

117784 次浏览

使用后备变量没有帮助。 Point类型是 Value 类型。

您需要将整个 Point 值分配给 Origin 属性:-

Origin = new Point(10, Origin.Y);

问题是,当您访问 Origin 属性时,get返回的是 Origin 属性自动创建字段中 Point 结构的副本。因此,您对 X 字段的修改不会影响基础字段。编译器检测到这一点,并给出一个错误,因为这个操作是完全无用的。

即使你使用你自己的支持变量,你的 get会看起来像:-

get { return myOrigin; }

您仍然会返回 Point 结构的一个副本,并且会得到相同的错误。

嗯... ... 更仔细地阅读了你的问题之后,也许你实际上是想直接从你的类中修改后备变量:-

myOrigin.X = 10;

是的,那就是你需要的。

这是因为 Point是一个值类型(struct)。

因此,当您访问 Origin属性时,您访问的是类所持有的值的 收到,而不是引用类型(class)中的值本身,所以如果您设置了 X属性,那么您就设置了副本上的属性,然后丢弃它,保留原始值不变。这可能不是你想要的,这就是为什么编译器会警告你。

如果只想更改 X值,则需要执行以下操作:

Origin = new Point(10, Origin.Y);

我猜这里的问题在于,您试图在语句中分配 object 的子值,而不是分配 object 本身。在这种情况下,您需要分配整个 Point 对象,因为属性类型是 Point。

Point newOrigin = new Point(10, 10);
Origin = newOrigin;

希望我说的有道理

问题是,您指向位于堆栈上的一个值,该值将不会反射回原属性,因此 C # 不允许您返回对值类型的引用。我认为你可以解决这个问题,通过删除的起源属性,而不是使用一个公共归档,是的,我知道这不是一个很好的解决方案。另一种解决方案是不使用 Point,而是创建自己的 Point 类型作为对象。

到目前为止,您已经知道错误的来源是什么。如果构造函数不存在,并且没有重载来获取您的属性(在本例中为 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;

这看起来是一件非常奇怪的事情,完全不推荐。只是列出一个替代方案。更好的方法是使 struct 不可变并提供一个合适的构造函数。

除了讨论结构与类的利弊之外,我倾向于从这个角度来看待目标和解决问题。

也就是说,如果您不需要在属性 get 和 set 方法后面编写代码(就像在您的示例中那样) ,那么简单地将 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

如下所示,只需删除属性“ 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]


}
}

我觉得很多人都糊涂了,这个特殊问题涉及到理解值类型 < em > properties 返回值类型的一个副本(与方法和索引器一样) ,并且值类型 < em > 字段 被直接访问。下面的代码通过直接访问属性的后备字段完成了您想要完成的任务(注意: 使用后备字段以冗长的形式表示属性相当于 auto 属性,但是在我们的代码中,我们可以直接访问后备字段) :

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;
}
}

您得到的错误是不理解属性返回值类型副本的间接后果。如果返回一个值类型的副本,并且没有将其赋值给局部变量,那么对该副本所做的任何更改都无法读取,因此编译器将其作为错误引发,因为这不是故意的。如果我们将副本分配给局部变量,那么我们可以改变 X 的值,但是它只会在局部副本上改变,这修复了编译时错误,但是不会产生修改 Origin 属性的预期效果。下面的代码说明了这一点,因为编译错误已经消失,但是调试断言将会失败:

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
}
}