今天编程的时候随手写下了大概像这样的代码 :
public struct Map { private int _Width; private int _Height; public int Width { get { return _Width; } set { _Width = value; } } public int Height { get { return _Height; } set { _Height = value; } } public Map(int width, int height) { this._Width = width; this._Height = height; } } public class Game { private Map _MyMap = new Map(0,0); public Map MyMap { get { return _MyMap; } set { _MyMap = value; } } public Game(){} public Game(int mapWidth, int mapHeight) { _MyMap = new Map(mapWidth,mapHeight); } } static void Main(string[] args) { Game game = new Game(); game.MyMap.Width = 12; }
然而ctrl+shift+B 却出现错误,build不过。心想这么简单的代码怎么可能有错误呢,开始还以为出现了拼写错误。仔细一看错误指向这一行game.MyMap.Width = 12; 错误提示为“Cannot modify the return value of 'ConsoleApplication1.Program.Game.MyMap' because it is not a variable” 。这时我才意识到由于Map是值类型,在.net中传递参数默认是按值传递,当我使用game.MyMap的时候,生成一个Map的临时对象并返回(这个临时对象是_MyMap的拷贝),然后如果修改这个临时对象的属性,这时就会出错,因为这个临时对象返回以后是会被丢弃的,修改它没有任何意义。
如果把struct改成class,编译就可以通过。因为这时候Map为引用类型,Game类中的_MyMap是一个Map类型的引用,它指向分配在托管堆上的Map对象。当使用game.MyMap的时候,也会返回一个临时对象,它是_MyMap的一个拷贝,它和_MyMap指向同一个对象。如果去修改这个对象的属性,自然是没有问题的。值得注意的是game.MyMap返回的是引用的拷贝,所以要避免写出下面这样的代码,原因已经很清楚了。
Game game = new Game(); Map map = game.MyMap; map = new Map(2,3);
其实原理都知道,但写代码的时候不细心,导致随手写下错误的代码,以后要多注意,养成写代码的好习惯。