7

(a struct )というTank名前のパブリック メンバーが 1 つあるという小さなクラスがあります。私が書くとき:LocationRectangle

Tank t = new Tank();
t.Location.X+=10;

すべてが正常に機能し、タンクが動きます。

しかし、メンバーをプロパティに変更した後は、この構文を使用できなくなりました。t.Locationは現在プロパティ(関数)であり、場所の一時コピーを返すため(値型であるため)、コンパイルされません。

私が今使用できる唯一の方法Locationは、次のようなことをすることです:

k = t.Location 
k.X +=10;
t.Location = k;

a+=10;この醜いコードを記述せず、直感的な構文を使用するのに役立つ回避策はありますか?

4

5 に答える 5

3

@ Servy から
「構造体は不変」です。いいえ、そうではありません。ほとんどの場合、それらはそうであるべきですが、本質的に不変ではありません。ここで固有の問題は、プロパティが構造体への参照ではなく、構造体のコピーを返すことです。C# に ref を返すための構文があれば、これは可能です。

これが機能しない根本的な理由は、構造体が不変であることです。それらが作成されたら、それで終わりです。このため、構造体を部分的に再割り当てすることはできません。足を交換しようとしているようなものです。できません。それはあなたの一部です。

あなたができる唯一のことは、次のような独自の X および Y 属性を実装することだと思います。

public double LocationX
{
   get
   {
       return Location.X;
   }
   set
   {
       Location = new Rectangle(value,Location.Y);
   }
}

明らかにこれを Y にもミラーリングする必要がありますが、これにより、必要なものが許可されるはずです (ただし、高速または効率的であるとは期待しないでください!)

これはあなたの差し迫った質問に答えますが、あなたのモデルについていくつかの点を挙げたいと思います. このような動きを更新しようとしないことを検討します。OO の観点からは、タンクは独自のオブジェクトであり、独自の位置を管理する必要があります。移動命令を与えて、自分の位置を更新させます。

例えば:

Tank.MoveRelative(10,0);   
Tank.MoveAbsolute(100,100);

これにより、もう少し自由度が増し、与えられたロジックに基づいて、タンクが行った要求を検証できるようになります。

于 2013-02-25T19:49:22.440 に答える
2

戦車を動かす方法を作ることをお勧めします。

public class Tank
{
    private Rectangle _location;

    public int X { get { return _location.X; } }
    public int Y { get { return _location.Y; } }

    public Tank(int width, int height /* other params */)
    {
        _location = new Rectangle(0, 0, width, height);
    }

    public Tank Move(Point offset)
    {
        _location.X += offset.X;
        _location.Y += offset.Y;

        return this;
    }
}

使用法は

var tank = new Tank(1, 1);
tank.Move(new Point(1, 1)).Move(new Point(1, 1)); //Tank would have X: 2, Y: 2

これは、使用するために変更することができますVector2

于 2013-02-25T20:00:24.947 に答える
2

この問題は、プロパティを使用して 2D および 3D 空間でプログラミングを開始するときにかなり頻繁に発生します。一般に、最善の回避策は、論理的な方法で加算される 2 つのベクトル構造または 2 つの異なる構造の間に加算を実装することです (あなたの場合、2D ベクトルと長方形の間に加算を実装して、その位置をオフセットします。 2 つの長方形を一緒に追加します)。

そうすることで、次のように書くことができます。

myTank.Location += new Vector2(10, 0);

これはまだ少し不格好ですが、1 回の操作で両方のコンポーネントを変更できます。理想的には、追加されたベクトルは、タンクの位置を更新するために使用する速度ベクトルです。

于 2013-02-25T19:54:06.543 に答える
0

主な違いは、プロパティが関数として分類され、フィールドが変数として分類されることです。関数メンバーの呼び出しが開始されます。

回避策は、プロパティではなくフィールドまたはバッキング ストアを使用することです。変更可能な値の型を作成することは避けるべきです。なぜなら、動作は驚くべきものであることが多く、予測が困難であり、時には完全に矛盾しているからです。

これは、発生している動作を説明するのに役立つ仕様の関連セクションです。

C# 4.0 セクション 1.6.7.2

set アクセサーは、value という名前の 1 つのパラメーターを持ち、戻り値の型を持たないメソッドに対応します。

get アクセサーは、プロパティ型の戻り値を持つパラメーターなしのメソッドに対応します。

次に、関連セクションである7.5.5 Function Member Invocationに切り替えます。

[関数メンバー] が値型で宣言されたインスタンス関数メンバーの場合:

[インスタンス式] が変数として分類されない場合、[インスタンス式] の型の一時ローカル変数が作成され、[インスタンス式] の vlue がその変数に割り当てられます。[インスタンス式] は、その一時変数への参照として再分類されます。一時変数は、[関数メンバー] 内でこのようにアクセスできますが、他の方法ではアクセスできません。 したがって、[インスタンス式] が真の変数である場合にのみ、呼び出し元は [関数メンバー] がこれに加えた変更を観察できます。

于 2013-02-25T20:27:54.170 に答える
0

クラスまたは構造体型の変数が値型フィールドを公開し、その値型がその内容をフィールドとして公開する場合、それらのフィールドに対する操作は、周囲の変数型のオペランドと同じくらい効率的に実行できます。値の型がプロパティとして公開されている場合、一般的にできる最善の方法は次のようなものです。

var temp = t.Location;
temp.X += 4;
t.Location = temp;

それほどエレガントではありませんが、比較的明確で、ひどく非効率的ではありません。AdjustLocation別の方法として、次のようなメソッドをタンクに公開させることもできます。

delegate void ActByRef<T1>(ref T1 p1);
void ActOnLocation(ActByRef<Point> proc)
  { proc(ref _Location); }

そしておそらくまた

delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
void ActOnLocation<PT1>(ActByRef<Point, PT1>, ref PT1 param1)
  { proc(ref _Location, ref param1); }

Locationこれらのメソッドは、プロパティが というバッキング フィールドを使用することを前提としています_Location。コードは次のようになります。

// Add 5 to X
myTank.ActOnLocation( (ref Point loc) => loc.X += 5 ); 

また

// Add YSpeed to Y
myTank.ActOnLocation( (ref Point loc, ref int param) => loc.Y += param, ref YSpeed);

後者の場合、ラムダ内では も も他のローカル変数も使用されないことに注意してYSpeedくださいthis。代わりに、パラメーターYSpeedとして渡されrefます。そのため、上記のコードが 100 万回実行されたとしても、システムはデリゲートを 1 つ生成するだけで済み、それを毎回再利用できます。

構造が大きい場合、上記のアプローチは、一時変数を使用するアプローチよりも高速になる可能性があります。オーバーヘッドはおそらく小さな構造体をコピーするコストよりも大きくなりますが、オーバーヘッドは構造体のサイズとは無関係です。上記のような構成を使用して一時的なコピーを作成する必要がないようにすれば、サイズが数キロバイトの構造体を効率的に使用できます。

于 2013-03-05T17:56:38.840 に答える