3

だから私は C# で最初の一歩を踏み出し、簡単なタイル パズルを作っていました。タイルの位置をモデル化していたとき、値のセマンティクスが必要でした。したがって、私が見る限り、これを行うには基本的に構造体を使用する方法とタプルを使用する方法の 2 つがあります。

タプルの場合、私のコードは次のようになります。

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);
    }
}

タプルはより簡潔ですが、H および V コンポーネント プロパティをそれらの周りに追加したにもかかわらず、パブリック API で混乱を招く可能性がありますItem1Item2

==構造体にはさらにコードが必要で、 and をオーバーライドしているため、 Equals と GetHashCode をオーバーライドする方法についてコンパイラの警告が表示されますが、そうすると!=、構造体を使用しても何も得られません (セマンティックおよび構文の観点から) ) 従来のクラスとまったく同じコードになるためです。

Item プロパティのノイズがないこと以外に、サブクラス化された Tuple よりも struc を使用する利点はありますか?

私のソリューションは両方とも期待どおりに動作しますか、それとも注意すべきニュアンスはありますか?

4

3 に答える 3

8

(余談ですが、IEquatable<TilePosition>ボクシングを避けるために、特に構造体の場合、両方のケースでも実装することをお勧めします。)

Item プロパティのノイズがないこと以外に、サブクラス化された Tuple よりも struc を使用する利点はありますか?

それが不変であることを考えると、どちらの場合も大まかに「値のセマンティクス」がありますが、それでも違いがあります...

  • クラス型のインスタンスには、ヒープ上にスペースが必要です (CLR によるエスケープ検出などがないことを前提としています)。構造体型の値は、場合によってはスタックのみを使用することがあります
  • クラス型の値を渡すことは、参照 (CLR アーキテクチャに応じて 4 バイトまたは 8 バイト) を渡すことを意味します。構造体型の値を渡すと、実際に値が渡されます (つまり 8 バイト)
  • クラス タイプの versionnullは有効な値です。TilePosition?構造体型のバージョンでは、存在しない可能性のある値を示すために使用する必要があります
  • 構造体バージョンnew TilePosition()では有効で、両方のフィールドの値が 0 になります (これは、フィールドと配列要素などのデフォルト値になります)
  • クラスを封印していないので、誰かが変更可能なサブクラスを作成する可能性があります。したがって、クライアントが完全に不変であると想定するのは安全ではありません。(封印した方がいいのかな…)
  • を使用する任意のコードでクラス型を使用できますがTuple<,>、構造体型の場合は明らかにそうではありません
  • willの意味は==、2 つのタイプの間で異なります。クラス型をオーバーロード==したとしても、呼び出し元は参照を比較するだけになる可能性があります。また、構造体の場合、ボックス化された参照を比較することになる可能性がありますが、役に立ちません。

もちろん、これらは単なる違いです。どちらのアプローチのメリットと見なされるかは、要件によって異なります。

于 2013-01-24T13:14:03.510 に答える
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;
}
于 2013-01-24T13:28:15.060 に答える