2009-03-21 39 views
15

C#でポストインクリメント演算子のオーバーロードに問題があります。整数を使用すると、次の結果が得られます。ポストインクリメント演算子オーバーロード

int n; 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(n++); // 10 
Console.WriteLine(n); // 11 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(++n); // 11 
Console.WriteLine(n); // 11 

しかし、クラスを使用して試してみると、オブジェクトが交換されているように見えます。古い値(10)を持つ新しいオブジェクト及び参照によって渡されたオブジェクトを返し、新しい値(11)を有する

アプリケーション、オーバーロードされた演算子メソッドをデバッグ
class Account 
{ 
    public int Balance { get; set; } 
    public string Name { get; set; } 

    public Account(string name, int balance) 
    { 
     Balance = balance; 
     Name = name; 
    } 

    public override string ToString() 
    { 
     return Name + " " + Balance.ToString(); 
    } 

    public static Account operator ++(Account a) 
    { 
     Account b = new Account("operator ++", a.Balance); 
     a.Balance += 1; 
     return b; 
    } 

    public static void Main() 
    { 
     Account a = new Account("original", 10); 

     Console.WriteLine(a); // "original 10" 

     Account b = a++; 

     Console.WriteLine(b); // "original 11", expected "operator ++ 10" 
     Console.WriteLine(a); // "operator ++ 10", expected "original 11" 
    } 
} 

、しかし最終的にはオブジェクトであります交換された。なぜこうなった?

+0

あなたは答えの後に質問を変更しないでくださいそれを見ている次の人にとっては混乱します。 – RedGlyph

+0

[X = X ++の違いは何ですか? vs X ++ ;?](http://stackoverflow.com/questions/226002/whats-the-difference-between-xx-vs-x) – nawfal

答えて

11

キーはどのように行Account b = a++;が機能しているか理解しています。あなたのコードが書かれているかを考えると、この行は、こののと同等です:

Account b = a; 
a++; 

そして、それはそれはで実行される順序で割り当てが効果的に(1)インクリメントの前に起こります。したがって、この行の最初の効果は、bの両方が元のオブジェクトを参照していることです。

++部分が評価されます。演算子メソッドの内部では、元のオブジェクトのBalanceをインクリメントします。この時点で、bはともにBalanceが11であり、bであることを示しています。

しかし、演算子メソッド内に新しいオブジェクトを作成し、それを演算子の出力として返しました。 は、新しく作成されたオブジェクトを指すように更新されます。

は現在新しいオブジェクトを指していますが、bは引き続きオリジナルを指しています。そのため、WriteLineの出力がスワップされたように見えます。

@MarkusQが指摘したように、++演算子はインプレース修正を行うことを意図しています。新しいオブジェクトを生成することによって、あなたはその前提を破っています。オブジェクトに対する演算子のオーバーロードは扱いにくい問題であり、これはなぜほとんどの場合避けがたいかの優れた例です。


1からだけで精度のために、オブジェクト上の演算子を扱うときに割り当てが実際にインクリメントする前に発生しませんが、最終的な結果は、この場合も同様です。実際には、元のオブジェクト参照がコピーされ、操作がオリジナルで実行され、コピーされた参照が左側の変数に割り当てられます。その割り当てが最初に起こったと思われるかどうかを説明するほうが簡単です。

本当に起こっているのはこのことです:原因++演算子は、オブジェクトにどのように動作するかを、この中

Account b = a++; 

結果、:

Account copy = a; 

Account x = new Account("operator ++", a.Balance); 
a.Balance += 1; // original object's Balance is incremented 
a = x; // a now points to the new object, copy still points to the original 

Account b = copy; // b and copy now point at the same, original, object 
+0

良い説明(私はそれが正しいかどうかはわかりませんが、それは理にかなっています:-)。いいえ、真剣に、これは良いことです。 +1。 – paxdiablo

+0

+1 pedantry用です。 – Brian

+0

オペレータメソッド本体の中に新しいオブジェクトを作成するのは良いことだと思います。新しいオブジェクトだけを変更し、与えられた元のオブジェクト( 'a'と呼ばれる)を変更しないでください。そのようにして、構造体のように動作します。 C#コンパイラは、事前またはポストのどちらかで正しく割り当てを行います。 –

13

私の最初の考えは、++の通常の意味がのインプレース修正であることを指摘していました。

public static Account operator ++(Account a) 
{ 
    a.Balance += 1; 
    return a; 
} 

これを模倣したい場合は、新しいオブジェクトを作成しないでください。

しかし、私はあなたがポストインクリメントを模倣しようとしていたことに気付きました。

私の2番目の考えは、 "それをしないでください"という意味です。セマンティクスはオブジェクト上にまったくマッピングされません。なぜなら、 "使用されている"値は実際には可変記憶場所ですからです。しかし、誰もがランダムな見知らぬ人によって "それをしないでください"と言われることを好むので、私はMicrosoft tell you not to do itとしましょう。そして、そのようなことについて、彼らの言葉が最終的なものであることを恐れる。

P.S. については、なぜそれが何をしている、あなたは本当にpreincrement演算子をオーバーライドしているとのようにの場合は、それはポストインクリメント演算子だった。

+1

ページは公開されておらず、存在しません。あなたはタイトルを覚えていますか、探してみませんか? – greenoldman

1

変更された値は常に返す必要があります。 C#はこれを新しい値として使用し、演算子に応じて古い値または新しい値を返します。

関連する問題