2011-11-13 13 views
9

私の脳がゼリーに変わったか、という経験がありましたなどです。私は少しこのようになりますクラス階層をいじってる:
enter image description here演算子と継承

マイMoneyクラスは次のようになります。

public abstract class Money 
{ 
    public int Amount { get; set; } 

    public static bool operator ==(Money leftSide, Money rightSide) 
    { 
     // Money can only be equal if it is in the same currency. 
     if (leftSide.GetType() != rightSide.GetType()) return false; 
     return leftSide.Amount == rightSide.Amount; 
    } 

    public static bool operator !=(Money leftSide, Money rightSide) 
    { 
     // If the currencies are different, the amounts are always considered unequal. 
     if (leftSide.GetType() != rightSide.GetType()) return true; 
     return leftSide.Amount != rightSide.Amount; 
    } 

    public static Money operator *(Money multiplicand, int multiplier) 
    { 
     var result = multiplicand * multiplier; 
     return result; 
    } 

    public static Dollar Dollar(int amount) 
    { 
     return new Dollar(amount); 
    } 

    public static Franc Franc(int amount) 
    { 
     return new Franc(amount); 
    } 
} 

マイドルoperator *次のようになります。

public static Dollar operator *(Dollar multiplicand, int multiplier) 
{ 
    var result = multiplicand.Amount * multiplier; 
    return new Dollar(result); 
} 

今、このテストコードを実行すると、スタックオーバーフロー(wahoo!)が発生します。

{ 
    Money fiveDollars = Money.Dollar(5); 
    Money timesTwo = fiveDollars*2; 
} 

これは、サブクラス(Dollar)operator *を再帰的に呼び出すと予想していました。これは、(Dollar * int)が非再帰的に定義されているため、明確な結果を返します。これは機能しないので、私は何かばかげたやり方をしたことです。なぜこれは機能しませんか?この行動を取る正しい方法は何でしょうか?

+1

スタックオーバーフローが発生したら、スタックを調べる必要があります。あなたは同じ機能が何度も何度も繰り返し呼び出しているのを見るでしょう。それだけで、何が起こっているのか、その理由について多くのことが分かります。 – abelenky

+2

再帰は、あなたが実際に 'Dollar.operator *'ではなく 'Money.operator *'を呼び出すために起こることに注意してください。演算子は*オーバーロード*され、オーバーライド*されないので、呼び出される関数は*実行時*型ではなくオペランドの*コンパイル時*型によって決まります。 'fiveDollars'は' Money'型の変数であるので、 'fiveDollars * 2'は' operator'の 'Money'バージョンを呼び出します(' fiveDollars'のランタイム*タイプは 'Dollar'です)。 – dlev

答えて

11

あなたは、問題はあなたが派生クラスで演算子をオーバーライドして、dynamic bindingが期待できることを期待していることである.Amount

public static Money operator *(Money multiplicand, int multiplier) 
{ 
    var result = multiplicand.Amount * multiplier; 
    return result; 
} 
+0

+1ありがとう、良いキャッチ –

4

を残しているように見えます。これはC#で動作する方法ではありません。演算子はにオーバーロードされ、であり、実際のオーバーロードはコンパイル時に選択されます。これは、次のコード再帰であり、自分自身を呼び出すことを意味する:

public static Money operator *(Money multiplicand, int multiplier) 
{ 
    var result = multiplicand * multiplier; 
    return result; 
} 

あなたは演算子のオーバーロードとオーバーライドの違いを見ることができる別の例は、これである。第3のケースで

int a = 5; 
int b = 5; 

Console.WriteLine(a == b); // true 
Console.WriteLine(a.Equals(b)); // true 
Console.WriteLine((object)a == (object)b); // false 
Console.WriteLine(((object)a).Equals((object)b)); // true 

、 C#では、整数の代わりにオブジェクトとしてabを扱うので、オブジェクトのために使用されるデフォルトの==演算子を使用します:参照の比較(この場合はボックス化された整数の参照)。

これにより、派生クラスの演算子を再定義するクラス階層で演算子を定義するのが厄介なことがあります。 C#(および他のほとんどのOOP言語)がmultiple dispatchのサポートを欠いているため、動作が両方のオペランドの組み合わせに依存するときは、特に厄介です。ビジターパターンを使用してこれを解決できますが、このケースでは、各通貨のサブクラスを使用することが最適な解決策であるかどうか再考する必要があります。

+0

+1はい、確かに問題があるようです。私は_did_演算子がオーバーライドされたと思う、私はここで何かを学んだ:) –

関連する問題