2013-01-24 82 views
3

所以我正在C#中開始我的第一步,並且正在製作一個簡單的拼圖遊戲。當我建模瓦片的位置時,我想要具有價值語義。所以,就我所見,基本上有兩種方法可以做到這一點,一個結構或一個元組。c#struct vs tuple中的值語義

元組的情況下,我的代碼看起來是這樣的:

public class TilePosition : Tuple<int,int> 
{ 
    public int HComponent{get { return Item1; }} 
    public int VComponent{get { return Item2; }} 

    public TilePosition(int horizontalPosition, int verticalPosition) 
     : base(horizontalPosition, verticalPosition) 
    { 
    } 
} 

的結構解決方案是這樣的:

public struct TilePosition 
{ 
    private readonly int hComponent; 
    private readonly int vComponent; 
    public int HComponent { get { return hComponent; } } 
    public int VComponent { get { return vComponent; } } 

    public TilePosition(int hComponent, int vComponent) 
    { 
     this.hComponent = hComponent; 
     this.vComponent = vComponent; 
    } 

    public static bool operator ==(TilePosition position1, TilePosition position2) 
    { 
     return position1.Equals(position2); 
    } 

    public static bool operator !=(TilePosition position1, TilePosition position2) 
    { 
     return !(position1 == position2); 
    } 
} 

元組是conciser但它暴露了Item1Item2這將混淆在一個公共API,即使我已經在它們周圍添加了H和V組件屬性。

該結構需要更多的代碼,我得到了一個關於如何重寫Equals和GetHashCode的編譯器警告,因爲我重寫了==!=,但如果我這樣做,我沒有從使用結構(從語義和句法的觀點),因爲它將與傳統類完全相同的代碼。

因此,除了沒有Item屬性的噪音之外,使用struc而不是使用子類Tuple還有什麼好處?

我的解決方案的行爲方式與我預期的一樣嗎?還是存在細微差別我應該注意?

+1

你爲什麼說從元組''<,>給你「值語義」獲得。 'Tuple <,>'類是一個引用類型,當然你的派生類也是一個引用類型。 –

+0

元組是一個引用類型,所以使用==比較兩個tile位置會比較引用,而不是X和Y座標的值。我認爲這不是你想要的? –

+0

這可能是他想要的,如果兩個瓷磚不允許佔據相同的空間。位置相同(也參考)意味着它們佔據相同的空間。 – MrFox

回答

8

(順便說一句,這將有利於實現在兩種情況下IEquatable<TilePosition>太 - 特別是在結構的情況下,爲了避免拳擊)

那麼,有沒有從使用STRUC在子類的任何好處除了沒有Item屬性的噪音之外,這個Tuple還有什麼?

由於它是不可變的,你必須在這兩種情況下大致「值語義」這兩種情況下,但是還是有區別的......

  • 類類型的實例需要在堆空間(假設CLR沒有逃跑檢測等);結構類型爲的值可能爲在某些情況下,只能使用堆棧
  • 傳遞類類型的值只是表示傳遞引用(4字節或8字節,取決於CLR體系結構);傳遞結構類型的值確實傳遞值(所以8字節)
  • 在類類型版本null是一個有效值;在結構類型版本中,您需要使用TilePosition?來指示可能不存在的值
  • 在結構體版本中,new TilePosition()是有效的,並且兩個字段的值都爲0(這將是默認值,例如對於字段和數組元素)
  • 因爲你還沒有密封你的類,所以有人可以創建一個可變的子類;因此它是而不是對於客戶端來說它是完全不可變的。 (你應該把它封好......)
  • 您可以與使用Tuple<,>任何代碼,使用你的類類型,而這顯然不是爲結構類型
  • ==意義將兩種類型之間的不同的情況。即使你在類類型中重載==,調用者仍然可能只是比較引用。並在結構的情況下,你仍然可以結束比較盒裝引用,無益。

這些只是差異當然 - 他們是否算作利於的一種方法或其他取決於您的需求。

+0

通過一個可變的子類,你的意思是一個具有可變多餘字段的類,或者是否有某種方式讓後代對我看不見的'xComponent'' int'進行變異? – Rawling

+0

如果在類中重載'==',那麼可能會調用'=='作爲引用類型'operator ==(object,object)'的'built-in'重載,如果重載解析在綁定時)挑選超載。我想這是你在最後一個項目中指出的。但是如果你在一個結構體中重載'==',可能會發生類似的事情:''operator ==(object,object)'重載可能被結構體的**裝箱值調用,盒子(或盒子),這是沒有用的。 –

+0

@JeppeStigNielsen:的確如此。將編輯最後一點。 –

0

如何使用鋸齒陣列並在每個字段上放置一個項目。這將更貼近瓷磚拼圖:

  • 瓷磚和他們的空間是1:1映射。所以每個瓷磚/空間只能有一個空間/瓷磚。
  • 無需比較拼貼組件。
  • 移動磚是容易的,例如

    if (fields[x, y] = null) 
        { 
         fields[x, y] = fields[oldX, oldY]; 
         fields[oldX, oldY] = null; 
        } 
    
0

你最好的選擇是做正確,並把所有的工作。如果你想有一個結構,而不是一個類(這可能是適合你),這裏有一個簡單的實現:

public struct TilePosition: IEquatable<TilePosition> 
{ 
    public TilePosition(int horizontalPosition, int verticalPosition) 
    { 
     _x = horizontalPosition; 
     _y = verticalPosition; 
    } 

    public int HComponent 
    { 
     get 
     { 
      return _x; 
     } 
    } 

    public int VComponent 
    { 
     get 
     { 
      return _y; 
     } 
    } 

    public static bool operator == (TilePosition lhs, TilePosition rhs) 
    { 
     return lhs.Equals(rhs); 
    } 

    public static bool operator != (TilePosition lhs, TilePosition rhs) 
    { 
     return !lhs.Equals(rhs); 
    } 

    public bool Equals(TilePosition other) 
    { 
     return (_x == other._x) && (_y == other._y); 
    } 

    public override bool Equals(object obj) 
    { 
     return obj is TilePosition && Equals((TilePosition)obj); 
    } 

    public override int GetHashCode() 
    { 
     unchecked 
     { 
      return (_x*397)^_y; 
     } 
    } 

    private readonly int _x; 
    private readonly int _y; 
} 
+0

是的。雖然我不認爲你真的需要'if(ReferenceEquals(null,obj)){return false; }部分。 –

+1

是因爲如果obj爲null,則「TilePosition」將返回false。注意:此代碼由Resharper自動生成。 :)我會刪除額外的測試(我可能會問JetBrains爲什麼Resharper把它放在...;) –