无法修改返回值错误c#

时间:2009-11-17 09:39:47

标签: c# variables struct immutability

我正在使用自动实现的属性。 我想解决以下问题的最快方法是声明我自己的支持变量吗?

public Point Origin { get; set; }

Origin.X = 10; // fails with CS1612
  

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

尝试修改作为中间表达式结果的值类型。由于该值未持久存在,因此该值将保持不变。

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

8 个答案:

答案 0 :(得分:163)

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

因此,当您访问Origin属性时,您正在访问类所拥有的值的副本,而不是像引用类型那样访问值本身( class),因此如果您在其上设置X属性,那么您将在副本上设置属性,然后将其丢弃,保持原始值不变。这可能不是你想要的,这就是编译器警告你的原因。

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

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

答案 1 :(得分:7)

使用支持变量无济于事。 Point类型是值类型。

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

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

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

即使您使用自己的支持变量,get也会如下: -

get { return myOrigin; }

你仍然会返回Point结构的副本,你会得到同样的错误。

嗯......更仔细地阅读了你的问题,或许你实际上意味着直接在你的班级内修改支持变量: -

myOrigin.X = 10;

是的,这就是你需要的。

答案 2 :(得分:4)

到目前为止,您已经知道错误的来源是什么。如果构造函数不存在带有重载的属性(在本例中为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不可变并提供适当的构造函数。

答案 3 :(得分:0)

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

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

希望我在那里有意义

答案 4 :(得分:0)

问题是您指向位于堆栈上的值,并且该值不会被重新传递回orignal属性,因此C#不允许您返回对值类型的引用。我想你可以通过删除Origin属性来解决这个问题,而不是使用公共文件,是的,我知道这不是一个好的解决方案。另一种解决方案是不使用Point,而是创建自己的Point类型作为对象。

答案 5 :(得分:0)

除了辩论结构与类的优缺点外,我倾向于从这个角度看待目标并解决问题。

话虽如此,如果您不需要在属性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

答案 6 :(得分:0)

只需按照以下步骤删除属性“ get set”,然后一切都会照常进行。

如果原始类型为instread,请使用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]

    }
}      

答案 7 :(得分:0)

我认为很多人对此感到困惑,这个特殊问题与理解值类型 properties 返回值类型的副本有关(如方法和索引器一样),和值类型字段可以直接访问。以下代码完全通过直接访问属性的后备字段来实现您想要实现的目的(请注意:用后备字段表示其冗长形式的属性等效于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
    }
}