我已经尝试了以下两个代码,它们似乎都以相同的方式工作。
// Case 1: ref T indexer
class Map {
Tile[,] tiles;
public ref Tile this[int x, int y]
=> ref tiles[x, y];
}
// Case 2: get/set indexer
class Map {
Tile[,] tiles;
public Tile this[int x, int y] {
get => tiles[x, y];
set => tiles[x, y] = value;
}
}
假设tiles
在构造函数中初始化,而Tile
是struct
。这两者之间有什么明显的区别吗?如果Tile
是class
?
答案 0 :(得分:2)
假设在构造函数中初始化了tile,而Tile是一个struct。这两者之间有什么明显的区别吗?
案例1
// Case 1: ref T indexer
class Map {
Tile[,] tiles;
public ref Tile this[int x, int y]
=> ref tiles[x, y];
}
您将ref
返回到索引Title
对象,以便您可以修改它,甚至将其设置为其他对象。这是C#7.0的一个新功能(正如您已标记的那样),它允许返回ref
,然后您也可以存储它。换句话说,会返回存储位置而不是值。
由于您要返回存储位置,因此可以将新的Tile
对象完全分配给索引项。如果没有ref
,您将只能修改它。
案例2
class Map {
Tile[,] tiles;
public Tile this[int x, int y] {
get => tiles[x, y];
set => tiles[x, y] = value;
}
}
以下是相同但没有C#7.0的简洁:
class Case2MapWithoutCSharp7
{
Tile[,] tiles;
public Tile this[int x, int y]
{
get { return tiles[x, y]; }
set { tiles[x, y] = value; }
}
}
由于Tile
是struct
,因此当您为其编制索引时,您将获得一份副本,如果您尝试此操作(假设Tile
具有属性X
):
map[0, 0].X = 10;
错误CS1612无法修改' Case2MapWithoutCSharp7.this [int,int]'的返回值因为它不是变量。
编译器抛出该错误以明确表明您认为自己在做什么(修改索引项),并不是您正在做的事情。您实际上正在修改副本,因此它会强制您执行此操作。因此,为了能够设置X
,您需要在这样的副本上执行此操作:
var tile = map[0, 0];
tile.X = 10;
您可以阅读有关该错误here的更多信息。
如果Tile是
class
怎么办?
在第一种情况下,由于您要返回存储位置,因此可以将新的Tile
对象完全分配给索引项。
在第二种情况下,如果没有ref
,因为class
对象通过引用传递,您将能够修改它,但不能将引用设置为一个全新的对象。所以你可以这样做:
map[0, 0].X = 2;
但如果你这样做了:
var tile = map[0, 0];
tile = new Tile();
tile.X = 5;
您显然正在改变一个全新的Tile
,而不是您编入索引的那个。