错误:";无法修改返回值";c#
我正在使用自动实现的属性。 我想解决以下问题的最快方法是声明我自己的支持变量错误:";无法修改返回值";c#,c#,variables,struct,immutability,C#,Variables,Struct,Immutability,我正在使用自动实现的属性。 我想解决以下问题的最快方法是声明我自己的支持变量 public Point Origin { get; set; } Origin.X = 10; // fails with CS1612 错误消息:无法修改“表达式”的返回值,因为 它不是一个变量 试图修改由错误导致的值类型 中间表达。由于该值未持久化,因此 将保持不变 若要解决此错误,请将表达式的结果存储在 中间值,或使用中间值的引用类型 表情 使用支持变量没有帮助。点类型是值类型 您需要将整个点值指定给原点特
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
错误消息:无法修改“表达式”的返回值,因为
它不是一个变量
试图修改由错误导致的值类型
中间表达。由于该值未持久化,因此
将保持不变
若要解决此错误,请将表达式的结果存储在
中间值,或使用中间值的引用类型
表情
使用支持变量没有帮助。点
类型是值类型
您需要将整个点值指定给原点特性:-
Origin = new Point(10, Origin.Y);
问题在于,当访问原点特性时,get
返回的是“原点特性自动创建”字段中点结构的副本。因此,修改此副本的X字段不会影响基础字段。编译器会检测到这一点,并给出一个错误,因为此操作完全无用
即使您使用自己的支持变量,您的get
也会如下所示:-
get { return myOrigin; }
您仍然会返回点结构的副本,并且会得到相同的错误
嗯。。。仔细阅读您的问题后,您可能实际上想直接在类中修改支持变量:-
myOrigin.X = 10;
是的,这正是您需要的。这是因为
点
是一种值类型(struct
)
因此,当您访问Origin
属性时,您访问的是类所持有的值的副本,而不是引用类型(class
)中的值本身,因此如果您在其上设置X
属性,则您在副本上设置属性,然后丢弃它,保持原始值不变。这可能不是您想要的,这就是编译器警告您的原因
如果只想更改X
值,则需要执行以下操作:
Origin = new Point(10, Origin.Y);
我想这里的问题是您试图在语句中分配对象的子值,而不是分配对象本身。在这种情况下,需要指定整个点对象,因为特性类型为点
Point newOrigin = new Point(10, 10);
Origin = newOrigin;
希望我在这里讲得通问题是,您指向堆栈上的一个值,该值不会重新影响到原始属性,因此C#不允许您返回对值类型的引用。我认为您可以通过删除Origin属性来解决这个问题,而是使用公共字段,是的,我知道这不是一个好的解决方案。另一种解决方案是不使用点,而是创建自己的点类型作为对象。现在您已经知道错误的来源。如果构造函数不存在,并且没有重载来获取您的属性(在本例中是
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;
这看起来很奇怪,根本不推荐。只是列出另一种方法。更好的方法是使结构不可变并提供适当的构造函数。除了讨论结构与类的优缺点之外,我倾向于从这个角度看待目标并解决问题 也就是说,如果您不需要在属性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
只需删除属性“getset”,如下所示,然后一切照常工作
如果是基元类型instread,则使用get;设置
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]
}
}
使用Microsoft.Xna.Framework;
使用制度;
名称空间DL
{
[可序列化()]
公共类摄影机属性
{
#区域[只读属性]
公共静态只读字符串CameraPropertyVersion=“v1.00”;
#endregion[只读属性]
///
///建造师
///
公共摄像机属性(){
//初始化
滚动=0f;
CameraPos=新矢量2(0f,0f);
}
#地区[物业]
///
///滚动
///
公共浮点滚动{get;set;}
///
///摄像机的位置
///
公共矢量2摄影机;
//而不是:公共向量2 CameraPos{get;set;}
#endregion[属性]
}
}
我认为很多人对此感到困惑,这一特定问题与理解值类型属性返回值类型的副本(与方法和索引器一样)以及直接访问值类型字段有关。下面的代码正是通过直接访问属性的支持字段来实现的(注意:用支持字段以详细形式表示属性等同于自动属性,但其优点是,在我们的代码中,我们可以直接访问支持字段):
您得到的错误是不理解属性返回值类型副本的间接结果。如果返回值类型的副本,并且未将其分配给局部变量,则无法读取对该副本所做的任何更改,因此编译器将此作为错误,因为这不是故意的。如果我们将副本分配给一个局部变量,那么我们可以更改X的值,但它只会在局部副本上更改,这会修复编译时错误,但不会产生预期的修改效果
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
}
}