2012-02-29 2 views
3

C#で複素数のクラスがありますが、加算演算子はこのように定義されています。C#は演算子のオーバーロードをどのように処理するのですか

public static Complex operator +(Complex c1, Complex c2) 
{ 
     return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary); 
} 

このように使用します。

Complex c1, c2, c3, c4; 

c1 = new Complex(...) 
... 
c4 = new Complex(...) 

Complex csum = c1 + c2 + c3 + c4; 

ここで私の質問は、C#コンパイラ+ランタイムがこれをどのように処理するかです。どうやら、このようなことをするようです。

ct1 = c1 + c2; // ct1 is a temporary object created by the compiler 
ct2 = ct1 + c3; 
csum = ct2 + c4; 

それとも、それがこのような(新しい一時オブジェクトの少ない創造)より良い方法でそれを行うことができます実現するのに十分なスマートです。

ct = c1 + c2; 
ct += c3; 
csum = ct + c4; 
+0

**コンパイラ**の仕事ではありません。 _compiler_はコンパイル時に魔法ですか?メモリ割り当ては実行時に 'CLR' /' JIT'ジョブ... – gdoron

+0

gdoron、コンパイラはこれをどのようにしてコードを生成するのでしょうか?あなたは好きです。 – MetallicPriest

+0

最適化されたバージョンではc4を忘れてしまいます。 –

答えて

1

単一の式は、名前付きローカル変数が存在するのとは別に、別々のステートメントと同等です(生成コードに関して)。 JITはそれを最適化するかもしれませんが、コンパイラはそれをすることはできません。 Complexが値型であるならば、本当にオブジェクト を割り当てる関与するつもりはないことに注意してください。

それは+演算子が(それは言語によって指定されています。それは、フレームワーク自体ではありません)C#でstringのために特別に処理されていることは注目に値します正確にこの種のものを避けるために:x + y + zがある(または少なくともすることができます)に変換一時的な文字列の作成を避けるためにstring.Concat(x, y, z)に入力してください。 「値型の値とは対照的に、ヒープ上に割り当てられた「オブジェクト」私はフィールドに続くオブジェクトヘッダ(型参照、シンクブロックなど)とメモリの領域を意味によって


これはデータ自体のみを含み、ヒープまたはスタック上に存在する可能性があります。スタック/ヒープの部分はもちろんのimplementation detailが、潜在的に重要なものです...と私はそれは「単なる値型の値」と「完全なオブジェクト」を区別することが合理的であると考えてい

+0

"実際にはオブジェクトの割り当てに関係しません":*すべて*は.NETのオブジェクトです。また、演算子の特別な扱いは、私が知る限り文字列にのみ適用されます。文字列クラスは、BCLに '+ '演算子を持つことさえできません。 – phoog

+0

@phoog:私は、文字列のために明示的に述べていることを意味しました。それは、私が作っていたポイントです。 "すべてがオブジェクトです" - 値の型の値とオブジェクトシンクブロックなど)。もちろん、ボクシングはもう一方に変わります。これに関して明確に定義された用語があればいいかもしれませんが、私はそれをどこでも見ていません。 –

+0

@phoog:説明脚注を追加しました。あなたの考えをご覧ください。 –

1

推測された段階的な手順は、コンパイルされた出力に関して正しくありません。 CLRはスタックマシンであり、4つすべてを一度に追加するコードでは、中間合計を名前付き変数として個別に割り当てる必要がないため、実行中の合計をその場で更新します(または先頭のスタック要素をポップしてプッシュします)。

ステップバイステップのバージョンでは、実際の操作と同じ数の操作が必要です。単項加算バージョンでは、ローカル変数の割り当てを1つ少なくするだけです。

以下は、4つの入力複素数を加算する3つの異なる方法により生成される実際のILあります。私のバージョンでは、Complexクラス(How to: Use Operator Overloading to Create a Complex Number Class (C# Programming Guide)から離したと仮定します)にintの代わりに2つのdoubleの値を保持するように変更しました。私は簡潔にするためにC#とILコードの両方でc1..c4の宣言を省略しました。要するに、推論された段階的アプローチの両方は、2つの余分なもの(変数リストに値をプッシュする)と、ldloc.s(変数リストから値を取得する)

オリジナル:

C番号:

Complex csum = c1 + c2 + c3 + c4; 

IL:

// MultipleAdd : 5 Locals, maxstack 3 
IL_0068: nop 
IL_0069: ldloc.0 
IL_006a: ldloc.1 
IL_006b: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0070: ldloc.2 
IL_0071: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0076: ldloc.3 
IL_0077: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_007c: stloc.s csum 
IL_007e: ret 

推論1:三バイナリ加算

C#の行のすべての4つの追加:

Complex ct1 = c1 + c2; 
Complex ct2 = ct1 + c3; 
Complex csum = ct2 + c4; 

IL:

// BinaryAdd : 7 Locals, maxstack 3 
IL_0068: nop 
IL_0069: ldloc.0 
IL_006a: ldloc.1 
IL_006b: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0070: stloc.s ct1 
IL_0072: ldloc.s ct1 
IL_0074: ldloc.2 
IL_0075: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_007a: stloc.s ct2 
IL_007c: ldloc.s ct2 
IL_007e: ldloc.3 
IL_007f: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0084: stloc.s csum 
IL_0086: ret 

推論2:単項加算

C番号:

Complex ct = c1 + c2; 
ct += c3; 
Complex csum = ct + c4; 

IL:

// UnaryAdd : 6 Locals, maxstack 3 
IL_0068: nop 
IL_0069: ldloc.0 
IL_006a: ldloc.1 
IL_006b: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0070: stloc.s ct 
IL_0072: ldloc.s ct 
IL_0074: ldloc.2 
IL_0075: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_007a: stloc.s ct 
IL_007c: ldloc.s ct 
IL_007e: ldloc.3 
IL_007f: call  valuetype Test.Complex Test.Complex::op_Addition(valuetype Test.Complex, 
                    valuetype Test.Complex) 
IL_0084: stloc.s csum 
IL_0086: ret 
+0

つまり、C#はすべての加算演算に対して新しいオブジェクトを作成します。右?または、C++の移動セマンティクスと同じように、同じ一時オブジェクトを再利用しますか? – MetallicPriest

+0

@ MetallicPriestそれはC#の問題ではなく、CIL/MSILの問題です。しかし、これは 'Complex :: op_Addition'にILを含めるべきだと言った:最後のステップ**は実際にはスタックから最後の2つの値をポップすることによって新しい' Complex'オブジェクト( 'newobj' op) - 2つの入力「実」フィールドと「複合」フィールドの加算結果。 –

関連する問題