Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
错误:";无法修改返回值";c#_C#_Variables_Struct_Immutability - Fatal编程技术网

错误:";无法修改返回值";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
    }
}