内部からのC#の増分

盗品に関するアカウントを持っていないRextorのリクエストで公開されました。

投稿の議論によってこの投稿を書くことを余儀なくされましたEvent \ Delegateに関する陰湿な質問()

増分順序の値(i ++または++ iよりも優れています)、およびそれが基本的に重要かどうか(生成されたオブジェクトと実行サイクルの数)。

メソッド呼び出しの引数ではなく、スタンドアロンのインクリメントについて書いていることをすぐに警告します。

構築方法(++ i); もちろん、i ++よりも常に優れています。 方法(i);



これを確認するために、これをチェックする単純なアプリケーションが作成されました。これはリリースとしてコンパイルされ(コンパイラが追加のブレークポイントを追加しないように)、Reflector Reflectorによって解析されます。

.netには、残りのすべてが作成される2つの主要なタイプ(ValueTypeとObject)しかないため、Int32と増分オーバーロードを持つクラスの2つのタイプが検討のために選択されました。 私は、他のすべてのタイプとオーバーロードのメソッドの動作が同じであることを提案することを敢えてします。

そして、アプリケーション自体をリストします:



メインメソッドのクラス:



class Program{

static void Main( string [] args){

Console .WriteLine ( " " );

int i = 0;

int j = 0;

i++;

++j;

Console .WriteLine(i);

Console .WriteLine(j);

Console .WriteLine(i++);

//

Console .WriteLine(i);

Console .WriteLine(++j);



SampleClass class_i= new SampleClass();

SampleClass class_j = new SampleClass();



class_i++;

++class_j;

Console .WriteLine(class_i);

Console .WriteLine(class_j);



Console .WriteLine(class_i++);

//

Console .WriteLine(class_i);

Console .WriteLine(++class_j);



Console .ReadLine();



}

}



* This source code was highlighted with Source Code Highlighter .






増分オーバーロードのあるクラス:

class SampleClass{

public SampleClass() {

num = 0;

}

int num;

// ++

public static SampleClass operator ++(SampleClass a){

return new SampleClass() {num=++a.num};

}

public override string ToString(){

return num.ToString();

}

}



* This source code was highlighted with Source Code Highlighter .






コンパイルされたプログラムはReflectorの拷問に送られました(もちろんILDasmをスキップすることはできましたが、それで作業するのは便利ではないと思います)。

Reflectorから、以下で説明するILコードが取得されました(投稿の最後に、完全なソースコードを含むファイルへのリンクが記載されています)。



最初の行は、このメソッドがアプリケーションにログインすることを示しています。



.entrypoint





メソッドで使用されるスタックのサイズを指定します。

.maxstack 3





そして、すべての変数を宣言する

.locals init (

[0] int32 i,

[1] int32 j,

[2] class SampleClass class_i, //

[3] class SampleClass class_j

)







次の行はテキストをロードして表示します: 「ほとんど有用なテキスト出力なし」 、この出力の理由は以下に開示されます

L_0000: ldstr "\u0411\u0435\u0437\u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442"

L_0005: call void [mscorlib]System. Console ::WriteLine( string )







次に、メソッドテキスト自体が始まり、その最初の部分はintを使用して操作を実行します。



変数値のリセット

//i=0

L_000a: ldc.i4.0 // 0

L_000b: stloc.0 // i



//j=0;

L_000c: ldc.i4.0 // 0

L_000d: stloc.1 // j







ここに増分があります

//i++

L_000e: ldloc.0 // i

L_000f: ldc.i4.1 // 1

L_0010: add //

L_0011: stloc.0 // i



//++j

L_0012: ldloc.1 //

L_0013: ldc.i4.1

L_0014: add

L_0015: stloc.1



* This source code was highlighted with Source Code Highlighter .






コードの前のセクションからわかるように、この場合の両方の変数の増分は同じように発生します



コードの次のセクションは、変数の値をコンソールに表示します。



// i j

L_0016: ldloc.0 //

L_0017: call void [mscorlib]System. Console ::WriteLine(int32)

L_001c: ldloc.1

L_001d: call void [mscorlib]System. Console ::WriteLine(int32)







最初の違いは、コンソールへの出力の瞬間にインクリメントするときに、後で現れます(最初にこのケースを考慮しないと書いたとき、私は少し嘘をつきました)

L_0022: ldloc.0 // i

L_0023: dup //

L_0024: ldc.i4.1 // 1

L_0025: add // 2

L_0026: stloc.0 // i



L_0027: call void [mscorlib]System. Console ::WriteLine(int32) //



// i (024-026)

L_002c: ldloc.0

L_002d: call void [mscorlib]System. Console ::WriteLine(int32)



// j++ ( i )

L_0032: ldloc.1

L_0033: ldc.i4.1

L_0034: add

L_0035: dup // , 22-27

L_0036: stloc.1

L_0037: call void [mscorlib]System. Console ::WriteLine(int32)



* This source code was highlighted with Source Code Highlighter .






次は、クラスで同じことを行うメソッドの部分です(正直なところ、コードがこれほど似ているとは思いませんでした)

// SampleClass (class_i)

L_003c: newobj instance void SampleClass::.ctor()

L_0041: stloc.2



// SampleClass (class_j)

L_0042: newobj instance void SampleClass::.ctor()

L_0047: stloc.3



// class_i ++

L_0048: ldloc.2

L_0049: call class SampleClass SampleClass::op_Increment( class SampleClass)

L_004e: stloc.2



// class_j

L_004f: ldloc.3

L_0050: call class SampleClass SampleClass::op_Increment( class SampleClass)

L_0055: stloc.3



// class_i

L_0056: ldloc.2

L_0057: call void [mscorlib]System. Console ::WriteLine( object )



// class_j

L_005c: ldloc.3

L_005d: call void [mscorlib]System. Console ::WriteLine( object )



// class_i ,

L_0062: ldloc.2

L_0063: dup

L_0064: call class SampleClass SampleClass::op_Increment( class SampleClass)

L_0069: stloc.2



//

L_006a: call void [mscorlib]System. Console ::WriteLine( object )

L_006f: ldloc.2

// , , L_0063: dup

L_0070: call void [mscorlib]System. Console ::WriteLine( object )



// class_j ,

L_0075: ldloc.3

L_0076: call class SampleClass SampleClass::op_Increment( class SampleClass)

L_007b: dup

L_007c: stloc.3

L_007d: call void [mscorlib]System. Console ::WriteLine( object )



// "Pres any Key to Contune.. ;)"

L_0082: call string [mscorlib]System. Console ::ReadLine()

L_0087: pop //

L_0088: ret //



* This source code was highlighted with Source Code Highlighter .






そして、約束されたとおり、コンソールにテキストを表示する最初の行があった理由を明らかにします。 実際、プログラムをコンパイルした後に(純粋なマシン命令で)違いがあるかどうかを確認することにしましたが、メソッドの初期化コードと宣言がマージされたため、メソッドと初期化コードを分離する行を挿入する必要がありました。

そのため、比較の結果、純粋なasmのコードもほぼ同じであることが判明しました。

インタガーを使用する場合、コードブロックは値をスタックにコピーし、1を追加し、保存して、1つのInc演算子に置き換えられました。

クラスの増分がある場合、呼び出しの順序が変更されます(ILコードのように)

ところで、asmaのソースコードも以下に示します。詳しく調べたい場合は、大歓迎です。



ILコード



アセンブリコード



アーカイブされたプロジェクト



All Articles