ref T indexer和get / set索引器之间有区别吗?

时间:2017-04-16 14:11:01

标签: c# ref c#-7.0

我已经尝试了以下两个代码,它们似乎都以相同的方式工作。

// 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在构造函数中初始化,而Tilestruct。这两者之间有什么明显的区别吗?如果Tileclass

,该怎么办?

1 个答案:

答案 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; }
    }
}

由于Tilestruct,因此当您为其编制索引时,您将获得一份副本,如果您尝试此操作(假设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,而不是您编入索引的那个。