21

多くのネイティブ コードと相互運用する必要があり、この場合、マーシャリングを必要としない安全でない構造体を使用する方がはるかに高速です。ただし、構造体に非プリミティブ型の固定サイズのバッファーが含まれている場合、これを行うことはできません。固定サイズのバッファがプリミティブ型のみであることが C# コンパイラの要件であるのはなぜですか? 次のような構造体で固定サイズのバッファを作成できないのはなぜですか。

[StructLayout(LayoutKind.Sequential)]
struct SomeType
{
  int Number1;
  int Number2;
}
4

3 に答える 3

21

C# の固定サイズ バッファーは、「不透明なクラス」と呼ばれる CLI 機能で実装されます。Ecma-335のセクション I.12.1.6.3 では、それらについて説明しています。

一部の言語は、マルチバイト データ構造を提供し、その内容は、アドレス算術および間接演算によって直接操作されます。この機能をサポートするために、CLI では指定されたサイズで値の型を作成できますが、データ メンバーに関する情報はありません。これらの「不透明なクラス」のインスタンスは、他のクラスのインスタンスとまったく同じ方法で処理されますが、ldfld、stfld、ldflda、ldsfld、および stsfld 命令を使用してそれらの内容にアクセスしてはなりません。

「データメンバーに関する情報がない」と「ldfld / stfldは使用されない」という問題があります。2 番目のルールは、構造体にキボッシュを配置します。メンバーにアクセスするには、ldfld と stfld が必要です。C# コンパイラは代替手段を提供できません。構造体のレイアウトはランタイム実装の詳細です。Decimal と Nullable<> も構造体であるため、アウトです。IntPtr は、そのサイズがプロセスのビット数に依存し、C# コンパイラがバッファーへのアクセスに使用される ldind/stind オペコードのアドレスを生成するのを困難にするため、アウトです。参照型参照は、GC がそれらを見つけることができる必要があり、最初の規則ではできないため、アウトです。列挙型には、基本型に応じて可変サイズがあります。解決可能な問題のように聞こえますが、なぜスキップしたのか完全にはわかりません。

これは、C# 言語仕様で言及されているもの (sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、または bool) を残すだけです。サイズが明確に定義された単純な型だけです。

于 2013-09-30T21:33:05.383 に答える
5

固定バッファとは何ですか?

MSDN から:

C# では、 fixed ステートメントを使用して、データ構造内に固定サイズの配列を持つバッファーを作成できます。これは、他の言語で記述されたコード、既存の DLL または COM プロジェクトなど、既存のコードを操作する場合に便利です。固定配列は、通常の構造体メンバーに許可されている任意の属性または修飾子を取ることができます。唯一の制限は、配列型がbool、byte、char、short、int、long、sbyte、ushort、uint、ulong、float、または double でなければならないことです。

固定バッファーが でなければならない理由について、Hans Passant 氏の言葉を引用しますunsafe。固定サイズのバッファー (配列) が安全でないのはなぜですか? 詳細については。

「固定バッファ」は実数配列ではないためです。これはカスタム値型であり、私が知っている C# 言語で値型を生成する唯一の方法です。配列のインデックス付けが安全な方法で行われたことを CLR が確認する方法はありません。コードも検証できません。これの最もグラフィックなデモンストレーション:

using System;

class Program {
    static unsafe void Main(string[] args) {
        var buf = new Buffer72();
        Console.WriteLine(buf.bs[8]);
        Console.ReadLine();
    }
}
public struct Buffer72 {
    public unsafe fixed byte bs[7];
}

この例では、スタック フレームに任意にアクセスできます。悪意のあるコードは、標準のバッファ オーバーフロー インジェクション手法を利用して、関数のリターン アドレスにパッチを適用し、コードを任意の場所に強制的にジャンプさせます。

はい、それは非常に危険です。

固定バッファーに非プリミティブ データ型を含めることができないのはなぜですか?

サイモン・ホワイトは有効な点を挙げました:

「コンパイラに追加された複雑さ」に行きます。コンパイラは、列挙可能な項目に適用される構造体に .NET 固有の機能が適用されていないことを確認する必要があります。たとえば、ジェネリック、インターフェイスの実装、非プリミティブ配列のさらに深いプロパティなどです。ランタイムにも、そのようなものとの相互運用の問題があることは間違いありません。

そしてイバサ:

「しかし、それはすでにコンパイラによって行われています。」一部のみ。コンパイラは、型が管理されているかどうかを確認するためのチェックを行うことができますが、固定バッファーに対して構造体を読み書きするためのコードの生成を処理しません。それは可能です (CIL レベルでそれを止めるものは何もありません)。C# で実装されていないだけです。

最後に、Mehrdad:

文字通り、固定サイズのバッファーを使用してほしくないからだと思います (マネージ コードを使用してほしいからです)。ネイティブ コードとの相互運用を簡単にしすぎると、すべてに .NET を使用する可能性が低くなり、マネージ コードを可能な限り促進したいと考えています。

答えは、「実装されていないだけです」という響きのようです。

なぜ実装されていないのですか?

私の推測では、コストと実装時間は彼らにとってそれだけの価値がないということです。開発者は、アンマネージ コードよりもむしろマネージ コードを推奨します。これは C# の将来のバージョンで実行される可能性がありますが、現在の CLR には必要な複雑さが欠けています。

別の方法として、セキュリティの問題が考えられます。固定バッファーは、コードに適切に実装されていない場合、あらゆる種類の問題やセキュリティ リスクに対して非常に脆弱であるため、C# のマネージド コードよりも固定バッファーの使用が推奨されない理由がわかります。使用を思いとどまらせたいものに多くの労力を費やすのはなぜですか?

于 2013-09-30T21:30:10.400 に答える