2017-04-16 21 views
2

我已經嘗試了以下兩種代碼,它們都似乎以相同的方式工作。ref T索引器和get/set索引器有區別嗎?

// 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

+0

只有當類型可變時纔有區別 –

+0

是的,差別很大。給平鋪一個X屬性並嘗試地圖[0,0] .X = 42; –

+0

哦,我明白了。情況2將不允許您修改其屬性。謝謝,我會和前者一起去。 :) – Dave

回答

2

假設瓷磚在構造函數中初始化,並且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; } 
    } 
} 

由於Tilestruct,當你索引它,你會得到一個副本,如果你試試這個(假設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,而不是一個你索引。