170

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

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

これまでのところ、次のようになっています。

  1. 末尾再帰
  2. Generic Co/Contravariance (C# 4 および VB 10 で許可)
  3. 戻り値の型だけが異なるオーバーロード
  4. アクセス修飾子をオーバーライドする
  5. System.Object から継承できないクラスがある
  6. フィルタリングされた例外 (VB および C# 6 で許可)
  7. 現在の静的クラス型の仮想メソッドを呼び出しています。
  8. ボックス化されたバージョンの値型のハンドルを取得します。
  9. 試行/障害を実行します。
  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 internalfamまたはassem ですが、C# 7.2 および VB 15.5 では許可されています)。
  24. <Module>グローバル関数またはモジュール初期化子を定義するためのクラスへの直接アクセス。
  25. 非ゼロ バインドの 1 ベースの配列を作成して使用する
  26. オープン インスタンスとクローズド静的デリゲート、およびゲッター/セッターのデリゲートを作成する
  27. 一時変数を使用せずに 2 つの値を交換する
  28. 任意の名前での明示的なインターフェイスの実装、および 2 つのインターフェイス関数の 1 つの実装 (VB で実行可能)
  29. 宣言vtfixup( externC に相当)
  30. 任意modoptまたは指定modreq
4

20 に答える 20

34

MSIL では、次の理由により、戻り値の型のみが異なるオーバーロードが許可されます。

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

また

callvirt int32 ...
于 2009-02-12T16:06:30.573 に答える
29

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

末尾再帰は、関数型言語で一般的な最適化です。これは、メソッド A がメソッド B の値を返すことによって終了し、メソッド 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 では手動で実装する必要があります。(ジッターは、この最適化を独自に自由に行うこともできますが、それはまったく別の問題です。)

于 2009-02-12T16:07:36.110 に答える
21

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

サンプル コード: ilasm.exe でコンパイルします。更新:アセンブラーが自動継承しないようにするには、"/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
于 2009-02-12T16:09:38.210 に答える
20

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

于 2009-02-12T16:14:06.767 に答える
18

ああ、私はその時これを見つけませんでした。(jon-skeet タグを追加すると可能性が高くなりますが、それほど頻繁にはチェックしません。)

すでにかなり良い回答を得ているようです。加えて:

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

また何か思いついたら追記します…

于 2009-02-26T16:06:58.060 に答える
17

CLR は既に一般的な共変/反変をサポートしていますが、C# は 4.0 までこの機能を取得しません。

于 2009-02-12T15:57:54.417 に答える
14

IL では、 から派生した型だけでなく、あらゆる型をスローしてキャッチできますSystem.Exception

于 2009-03-13T16:53:57.467 に答える
10

IL には、仮想メソッド呼び出しのcallとの区別があります。前者を使用すると、動的クラス型の仮想関数ではなく、現在の静的クラス型callvirtの仮想メソッドを強制的に呼び出すことができます。

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!"; }
}

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

于 2009-02-23T16:24:42.053 に答える
9

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

このVB.NETの例は、http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspxWhen ShouldCatch(ex) = Trueから抜粋したものです(キャッチ句):

Try
   Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
   Console.WriteLine("Caught exception!")
End Try
于 2009-02-12T16:11:49.080 に答える
9

try/catch では、try ブロック自体の catch ブロックから再入力できます。したがって、これを行うことができます:

.try {
    // ...

  MidTry:
    // ...

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

RestOfMethod:
    // ...

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

于 2011-01-18T11:02:19.080 に答える
8

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

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

于 2009-10-16T13:08:52.863 に答える
7

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 と呼ばれていないメソッドに適用できます。

仕様を読んでください。他にもいくつか見つかると思います。

于 2009-02-26T22:53:00.737 に答える
6

C# では許可されていないメソッド オーバーライド co/contra-variance をハックすることができます (これは一般的な分散と同じではありません!)。これを実装するための詳細については、こちら、およびパート12を参照してください。

于 2010-08-03T15:49:39.703 に答える
4

私が(完全に間違った理由で)望んでいたのは、列挙型の継承だったと思います。SMIL で実行するのは難しいことではないように思えますが (Enum は単なるクラスであるため)、C# 構文では実行する必要はありません。

于 2009-03-02T15:19:04.913 に答える
4

さらにいくつかあります:

  1. デリゲートに追加のインスタンス メソッドを含めることができます。
  2. デリゲートはインターフェイスを実装できます。
  3. デリゲートとインターフェイスに静的メンバーを含めることができます。
于 2009-06-03T04:49:41.030 に答える
3

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

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

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

===

質問を編集してこれを#20としてリストに追加するのに十分なstackoverflow street credzがありません。他の誰かがそれを膨らませることができれば、;-)

于 2009-10-11T16:17:04.653 に答える
2

IL ではモジュール レベル (別名グローバル) メソッドを定義することもできます。対照的に、C# では、メソッドが少なくとも 1 つの型に関連付けられている場合にのみメソッドを定義できます。

于 2011-02-17T06:11:39.687 に答える
2

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

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

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

于 2010-12-06T17:58:51.363 に答える
2

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

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

public class YourCustomDelegate : MulticastDelegate { }

于 2011-02-17T05:58:34.217 に答える