假设瓷砖在构造函数中初始化,并且Tile是一个结构。两者之间有什么明显的区别?
CASE 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
,你只能修改它。
CASE 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。
如果瓷砖是class
怎么办?
在第一种情况下,由于您要返回存储位置,因此您可以完全将新的Tile
对象分配给索引项。
在第二种情况下,如果没有ref
,因为class
对象是通过引用传递的,所以您将能够修改它,但不能将引用设置为整个新对象。所以,你可以这样做:
map[0, 0].X = 2;
但是,如果你这样做:
var tile = map[0, 0];
tile = new Tile();
tile.X = 5;
显然你是变异了一个全新Tile
,而不是一个你索引。
只有当类型可变时才有区别 –
是的,差别很大。给平铺一个X属性并尝试地图[0,0] .X = 42; –
哦,我明白了。情况2将不允许您修改其属性。谢谢,我会和前者一起去。 :) – Dave