web-dev-qa-db-ja.com

C#やVB.NETではできないMSILでできることは何ですか?

.NET言語で記述されたすべてのコードはMSILにコンパイルされますが、MSILを直接使用してのみ実行できる特定のタスク/操作はありますか?

また、MSILでC#、VB.NET、F#、j#、その他の.NET言語よりも簡単に処理できるようにします。

これまでのところこれがあります:

  1. テール再帰
  2. ジェネリックCo/Contravariance
  3. 戻り型のみが異なるオーバーロード
  4. アクセス修飾子を上書きする
  5. System.Objectから継承できないクラスがある
  6. フィルタリングされた例外(vb.netで実行できます)
  7. 現在の静的クラス型の仮想メソッドを呼び出します。
  8. 値型のボックス版のハンドルを取得します。
  9. Try/faultを実行します。
  10. 禁止された名前の使用。
  11. 値タイプ用の独自のパラメーターなしのコンストラクターを定義
  12. raise要素を使用してイベントを定義します。
  13. CLRでは許可されているが、C#では許可されていない一部の変換。
  14. main()メソッドを.entrypointとして作成します。
  15. ネイティブintおよびネイティブunsigned int型を直接操作します。
  16. 一時的なポインタで遊ぶ
  17. methodBodyItemのemitbyteディレクティブ
  18. System.Exception以外のタイプをスローしてキャッチする
  19. 列挙型の継承(未検証)
  20. バイトの配列を(4倍小さい)intの配列として扱うことができます。
  21. フィールド/メソッド/プロパティ/イベントにすべて同じ名前を付けることができます(未確認)。
  22. 独自のcatchブロックからtryブロックに戻ることができます。
  23. Famandassemアクセス指定子にアクセスできます(protected internal is famorassem)
  24. グローバル関数またはモジュール初期化子を定義するための<Module>クラスへの直接アクセス。
164
Binoj Antony

MSILは、戻り値の型のみが異なるオーバーロードを許可します。

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

または

callvirt int32 ...
34
Anton Gogolev

C#およびVBを含むほとんどの.Net言語は、MSILコードの末尾再帰機能を使用しません。

テール再帰は、関数型言語で一般的な最適化です。メソッドBの呼び出しが行われるとメソッドAのスタックの割り当てを解除できるように、メソッドBの値を返すことによってメソッドAが終了すると発生します。

MSILコードはテール再帰を明示的にサポートしており、一部のアルゴリズムでは、これは重要な最適化になる可能性があります。ただし、C#およびVBはこれを行うための命令を生成しないため、手動で(またはF#または他の言語を使用して)実行する必要があります。

C#で末尾再帰を手動で実装する方法の例を次に示します。

private static int RecursiveMethod(int myParameter)
{
    // Body of recursive method
    if (BaseCase(details))
        return result;
    // ...

    return RecursiveMethod(modifiedParameter);
}

// Is transformed into:

private static int RecursiveMethod(int myParameter)
{
    while (true)
    {
        // Body of recursive method
        if (BaseCase(details))
            return result;
        // ...

        myParameter = modifiedParameter;
    }
}

ローカルデータをハードウェアスタックからヒープに割り当てられたスタックデータ構造に移動することにより、再帰を削除するのが一般的です。上記のような末尾呼び出しの再帰除去では、スタックが完全に除去されます。これは非常に優れた最適化です。また、戻り値は長い呼び出しチェーンをたどる必要はありませんが、直接返されます。

しかし、いずれにせよ、CILはこの機能を言語の一部として提供しますが、C#またはVBを使用すると、手動で実装する必要があります(ジッターは、この最適化を自由に行うこともできます。しかし、それはまったく別の問題です。)

29

MSILでは、System.Objectから継承できないクラスを持つことができます。

サンプルコード:ilasm.exeでコンパイルPDATE:アセンブラが自動継承しないようにするには、「/ NOAUTOINHERIT」を使用する必要があります。

// Metadata version: v2.0.50215
.Assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.Assembly sample
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module sample.exe
// MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x02F20000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Hello
{
  .method public hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World!"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Hello::Main
} // end of class Hello
21
Ramesh

protectedおよびinternalアクセス修飾子を組み合わせることが可能です。 C#では、protected internalメンバーは、アセンブリおよび派生クラスからアクセスできます。 MSILを介して、アセンブリ内の派生クラスからアクセス可能なメンバーを取得できますonly。 (私はそれがかなり役に立つかもしれないと思います!)

20
yatima2975

ああ、その時私はこれに気づかなかった。 (あなたがjon-skeetタグを追加する場合、それはより可能性が高いですが、私はそれを頻繁にチェックしません。)

あなたはすでにかなり良い答えを持っているようです。加えて:

  • C#では、値型のボックス版のハンドルを取得できません。あなたはC++/CLIでできます
  • C#でtry/faultを実行することはできません(「fault」は「すべてをキャッチしてブロックの最後で再スローする」または「最終的には失敗時のみ」のようなものです)
  • C#で禁止されている多くの名前がありますが、合法的なIL
  • ILでは 値型の独自のパラメーターなしのコンストラクターを定義する を使用できます。
  • C#では、「raise」要素を使用してイベントを定義することはできません。 (VB youhavetoでカスタムイベントの場合は、 "default"イベントには含まれません。)
  • 一部の変換はCLRでは許可されていますが、C#では許可されていません。 C#でobjectを使用すると、これらが機能する場合があります。例は int []/int [] SO question を参照してください。

他に何か考えたらこれに追加します...

18
Jon Skeet

CLRはすでに一般的な共分散をサポートしていますが、C#は4.0までこの機能を利用できません

17
ermau

ILでは、System.Exceptionから派生したタイプだけでなく、あらゆるタイプをスローおよびキャッチできます。

14

ILは、仮想メソッド呼び出しについて、callcallvirtを区別しています。前者を使用すると、動的クラスタイプの仮想関数の代わりに、現在の静的クラスタイプの仮想メソッドを強制的に呼び出すことができます。

C#にはこれを行う方法がありません。

_abstract class Foo {
    public void F() {
        Console.WriteLine(ToString()); // Always a virtual call!
    }

    public override string ToString() { System.Diagnostics.Debug.Assert(false); }
};

sealed class Bar : Foo {
    public override string ToString() { return "I'm called!"; }
}
_

ILと同様に、VBはMyClass.Method()構文を使用して非仮想呼び出しを発行できます。上記では、これはMyClass.ToString()になります。

10
Konrad Rudolph

Try/catchでは、独自のcatchブロックからtryブロックに再び入ることができます。だから、これを行うことができます:

.try {
    // ...

  MidTry:
    // ...

    leave.s RestOfMethod
}
catch [mscorlib]System.Exception {
    leave.s MidTry  // branching back into try block!
}

RestOfMethod:
    // ...

私の知る限り、C#またはVBではこれを行うことはできません

9
thecoop

ILおよびVB.NETを使用すると、例外をキャッチするときにフィルターを追加できますが、C#v3はこの機能をサポートしていません。

このVB.NETの例は http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care .aspx (Catch句のWhen ShouldCatch(ex) = Trueに注意してください):

Try
   Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
   Console.WriteLine("Caught exception!")
End Try
9
Emanuele Aina

私の知る限り、C#で直接モジュール初期化子(モジュール全体の静的コンストラクタ)を作成する方法はありません。

http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx

8
yoyoyoyosef

Native types
ネイティブintおよびネイティブunsigned int型を直接操作できます(c#では、同じではないIntPtrでのみ操作できます。

Transient Pointers
一時的なポインターを操作できます。一時的なポインターは、マネージ型へのポインターですが、マネージヒープにないため、メモリ内で移動しないことが保証されています。アンマネージコードをいじらずに、これをどのように有効に利用できるかは完全にはわかりませんが、stackallocなどの手段を通じて他の言語に直接公開されることはありません。

<Module>
必要に応じて、クラスをいじることができます(ILを必要とせずに、リフレクションによってこれを行うことができます)

.emitbyte

15.4.1.1 .emitbyteディレクティブMethodBodyItem :: =…| .emitbyte Int32このディレクティブにより、ディレクティブが出現するポイントで、符号なし8ビット値がメソッドのCILストリームに直接出力されます。 [注:.emitbyteディレクティブはテストの生成に使用されます。通常のプログラムの生成には必要ありません。エンドノート]

.entrypoint
これにはもう少し柔軟性があります。たとえば、Mainと呼ばれないメソッドに適用できます。

spec を読んでください。さらにいくつか見つけることができると思います。

7
ShuggyCoUk

C#で許可されていないメソッドオーバーライドco/contra-varianceをハックできます(これは一般的な分散と同じではありません!)。これの実装に関する詳細情報 here とパート 1 および 2 があります

6
thecoop

ここにいくつかあります:

  1. デリゲートに追加のインスタンスメソッドを含めることができます。
  2. デリゲートはインターフェイスを実装できます。
  3. デリゲートとインターフェイスに静的メンバーを含めることができます。
4
leppie

私がずっと望んでいたのは(完全に間違った理由で)Enumでの継承だったと思います。 (Enumsは単なるクラスであるため)SMILで行うのは難しいことではないように見えますが、C#構文で求められていることではありません。

4
Shalom Craimer

難読化ツールが使用するもの-フィールド/メソッド/プロパティ/イベントをすべて同じ名前にすることができます。

3
Jason Haley

20)バイトの配列を整数の(4倍小さい)配列として扱うことができます。

CLRのxor関数はintで動作し、バイトストリームでXORを実行する必要があったため、最近これを使用して高速のXOR実装を実行しました。

結果のコードは、C#で実行される同等のコードよりも約10倍高速であると測定されました(各バイトでXORを実行))。

===

質問を編集し、これを#20としてリストに追加するのに十分なstackoverflowストリートcredzがありません。

3
rosenfield

列挙型の継承は実際には不可能です。

Enumクラスから継承できます。ただし、結果は特にEnumのようには動作しません。値型のようにではなく、通常のクラスのように動作します。奇妙なことは:IsEnum:True、IsValueType:True、IsClass:False

しかし、それは特に便利ではありません(人またはランタイム自体を混乱させたくない場合を除きます)。

2
Zotta

ILでSystem.Multicastデリゲートからクラスを派生させることもできますが、C#ではこれを行うことはできません。

//次のクラス定義は不正です:

パブリッククラスYourCustomDelegate:MulticastDelegate {}

2
plaureano

ILおよびC#でモジュールレベル(別名グローバル)メソッドを定義することもできます。対照的に、少なくとも1つの型にアタッチされているメソッドのみを定義できます。

1
plaureano