无法修改返回值错误c#

问题描述:

我正在使用自动实现的属性。 我想最快的方法来解决以下是宣布我自己的支持变量?无法修改返回值错误c#

public Point Origin { get; set; } 

Origin.X = 10; // fails with CS1612 

错误消息:无法修改“表达”,因为 它不是一个可变

试图修改,这是一个中间结果的值的类型的返回值表达。由于该值不是持久的,因此该值将保持不变。

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

+12

这是又一个为什么可变值类型是一个坏主意的例子。如果您可以避免突变某个值类型,请这样做。 – 2009-11-17 15:55:50

+0

采取以下代码(从我的努力在一个特定的EL :-)博客的AStar实现,这无法避免更改值类型: class Path :IEnumerable 其中T:INode,new(){... (新的点(x,y)){} 路径 path = new路径(新的T(x,y));} public HexNode(int x,int y) // Error // Ugly fix Path path = new Path (new T()); path.LastStep.Centre = new Point(x,y); – 2017-04-14 17:26:32

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

正因为如此,当您访问您所访问的Origin属性复制由类持有的价值,而不是本身的价值,你会与一个引用类型(class),因此,如果您设置的X属性,然后你在副本上设置属性,然后丢弃它,保持原来的值不变。这可能不是你想要的,这就是为什么编译器会警告你。

如果你想改变只是X值,你需要做的是这样的:

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

@Paul:你有能力把结构改成一个类吗? – Doug 2014-06-27 15:26:19

+0

这是一种令人失望的原因,因为属性设置器im分配具有副作用(该结构充当后备引用类型的视图) – Alexander 2017-05-01 21:50:02

+0

另一种解决方案是简单地将您的结构变成一个类。与C++不同,C++中的类和结构只有默认成员访问(分别为private和public)不同,C#中的结构和类存在一些差异。这里有一些更多信息:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/ – Artorias2718 2017-11-10 00:02:05

使用备份变量将无济于事。 Point类型是一个值类型。

您需要分配整点价值的原产地属性: -

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

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

即使你用你自己的后盾变量您get看起来像: -

get { return myOrigin; } 

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

嗯...有更多的仔细阅读你的问题也许你实际上意味着直接从您的类中修改后盾变量: -

myOrigin.X = 10; 

是,这将是你所需要的。

我想这里的缺点是,你要转让对象的子值的语句,而不是分配对象本身。在这种情况下,您需要指定整个Point对象,因为属性类型是Point。

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

希望我是有道理的有

+2

重要的一点是,Point是一个结构体(valuetype)。如果它是一个类(对象),那么原始代码就可以工作。 – 2009-11-17 14:21:40

+0

Correctamundo - 这可能是一个很好的面试问题。 – MSIL 2009-11-18 04:08:59

+0

@HansKesting:如果'Point'是一个可变类类型,原始代码将在属性Origin中返回的对象中设置字段或属性'X'。我认为没有理由相信这会对包含“Origin”属性的对象产生预期的影响。某些框架类具有将其状态复制到新的可变类实例并返回它们的属性。这样的设计的好处是允许诸如'thing1.Origin = thing2.Origin;'这样的代码来设置对象的来源的状态以匹配另一个的来源,但是它不能警告诸如'thing1.Origin.X + = 4;'。 – supercat 2013-04-25 18:49:33

的问题是,你点到位于堆栈和值上的值不会relfected回一部开拓创新属性,因此C#不允许你返回引用一个值类型。我想你可以通过删除Origin属性来解决这个问题,而是使用公共提交的,是的,我知道这不是一个好的解决方案。另一种解决方案是不使用Point,而是将您自己的Point类型创建为对象。

+0

如果'Point'是引用类型的成员,那么它将不在堆栈上,它将位于包含对象内存中的堆上。 – 2009-11-17 15:33:02

现在您已经知道错误的来源。如果一个构造函数不存在一个超载来取得你的属性(在这种情况下为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; 

这看起来像一个非常奇怪的事情做,不建议。只列出一个替代方法。更好的方法是使结构不可变,并提供适当的构造函数。

+2

难道不会污染'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);' 。后者似乎对我来说真的很难受。 – supercat 2013-04-25 15:38:29

+0

@supercat哦,是的,忽略了!我会编辑它 – nawfal 2013-04-25 16:41:20

+0

@supercat这是我第一次想到你的观点,**很有意义!**你有替代模式/设计理念来解决这个问题吗?如果C#在默认情况下没有为结构提供默认构造函数会更容易(在这种情况下,我必须将'X'和'Y'传递给特定的构造函数)。现在,当我们可以执行'Point p = new Point()'时,它就失去了点。我知道为什么它真的需要一个结构,所以没有必要去思考它。但是,你是否有一个很酷的想法来更新一个像'X'这样的属性? – nawfal 2013-04-25 16:57:51