連載:C# 2.0入門

最終回 小さな改善とコンパイラの新機能、そして3.0への展望

株式会社ピーデー 川俣 晶
2008/01/11
Page1 Page2 Page3

固定サイズ・バッファ

 できるだけ避けて通りたいが、まれにどうしても避けられなくなるのが「unsafeコンテキスト」である。unsafeコンテキストとはポインタを使用できるunsafeコードを記述できる文脈を示す。

 プログラムとは、実現すべき機能を実現するところから価値が生まれるのであって、そのために避けて通れないことは受け入れるしかない。理想論だけでプログラムは書けないのである。そのような意味で「unsafeコンテキストのような危険な機能が存在することはおかしい」という批判は的外れといえる。unsafeコンテキストに類する機能を持たないプログラミング言語を使って書く場合は、結局、より危険な別言語でその個所を記述してプログラムに取り込んで使うしかない。例えばC言語で書いたモジュールを取り込むぐらいなら、C言語と比較して飛躍的に安全度が高いC#のunsafeコンテキストを使う方が、はるかに安心といえる。

 さて、そうはいってもunsafeコンテキストはさまざまな面倒を抱え込んでいる。特に外部とデータをやりとりする場合の「型のマーシャリング」は面倒なことになる。それを支援するためにさまざまな機能が用意されているのだが、C# 1.xでは決定的に足りないものがあった。それは、構造体の中に固定サイズの配列を埋め込んだ形で宣言できないことだった。

 例えば、WindowsのAPIが定義して使用する(C/C++で定義された)構造体には、配列が埋め込まれていることは珍しくない。特に、データの名前として文字列を格納する場合は、文字列を格納するための配列があって当たり前といえる。

 しかし、C# 1.xは構造体の内部に配列を埋め込むことができない。なぜかといえば、C#の配列は、配列単体で独立したオブジェクトとして扱われるためである。構造体の中に入れることができるのは、配列オブジェクトへの参照であって、配列そのものではない。

 例えばWin32 APIで論理フォントを指定するLOGFONT構造体は、フォントサイズなどの数値と並んでフォント名を格納する配列を含む。このような構造体はそのままC# 1.xで記述することができなかったのである。これに対処するため、文字列に変換するマーシャリング指定を行うためにMarshalAs属性を埋め込むなどの方策が取られていたが、万全とはいえない。マーシャリング機能が提供する変換機能の枠を超えてしまうと扱えないし、マーシャリング指定を誤るとそれだけでプログラムが暴走する可能性も存在するのである。

 そこで、C# 2.0では固定サイズ・バッファという機能を用意している。

 固定サイズ・バッファは、C# 1.xにも存在したfixedステートメントによる「移動されない変数」を構造体に拡張したものといえる(移動されないという特徴は固定サイズ・バッファにも適用される)。

 使い方は簡単で、配列を以下のようにfixedキーワードを付けて宣言するだけである。これで、配列のサイズを直接書き込んで指定することが可能になる。

public fixed int AnArray[100];

 配列の型はbool、byte、char、short、int、long、sbyte、ushort、uint、ulong、float、doubleのみが使用できる。

 さて、実際に使用した例として、リスト1の「API経由でフォント名を取得する」を用意した。C# 1.xを用いて実現する場合、通常はMarshalAs属性が不可欠となる事例である。

 ちなみに、このサンプル・プログラムではフォント名はushort型の配列として取得され、文字列オブジェクトとしては取得できていないことにも注意を払っていただきたい。MarshalAs属性を使えば文字列として受け渡すこともできるが、固定サイズ・バッファを使うとそれはできないのである。ありのままの構造体を受け渡すとは、そういうことである。

 なお、リスト1はVisual Studio 2005でWindowsアプリケーションのプロジェクトを作成し、Labelコントロールをフォームに貼ったうえで、プロジェクトのプロパティの[ビルド]タブにある「アンセーフコードの許可]にチェックを入れた状態で動作可能となる。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsApplication
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    public const int LF_FACESIZE = 32;

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct LOGFONT
    {
      public int lfHeight;
      public int lfWidth;
      public int lfEscapement;
      public int lfOrientation;
      public int lfWeight;
      public byte lfItalic;
      public byte lfUnderline;
      public byte lfStrikeOut;
      public byte lfCharSet;
      public byte lfOutPrecision;
      public byte lfClipPrecision;
      public byte lfQuality;
      public byte lfPitchAndFamily;
      public fixed ushort lfFaceName[LF_FACESIZE]; // 固定サイズ
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      object boxedLogFont = new LOGFONT();
      this.Font.ToLogFont(boxedLogFont);

      LOGFONT logFont = (LOGFONT)boxedLogFont;
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < LF_FACESIZE; i++)
      {
        unsafe // unsafeコンテキスト
        {
          if (logFont.lfFaceName[i] == 0) break;
          sb.Append((char)logFont.lfFaceName[i]);
        };
      }
      label1.Text = sb.ToString();
    }
  }
}
リスト1 Win32 API経由でフォント名を取得する

 論理フォントを取得するためのFontクラスのToLogFontメソッドは、クラス・ライブラリが提供するメソッドであるにもかかわらず、その引数の型の定義はクラス・ライブラリに含まれておらず、object型を受け渡すようになっている。そのため、プログラマーが自分で論理フォント(LOGFONT)構造体の定義を用意しなければならない。

volatileがIntPtr型およびUIntPtr型へ適用できる

 難しい話ではないので、要約のみ説明する。

 volatileキーワードは、マルチスレッドのプログラミングなどを行うとき、そのスレッド外の要因でいつ書き換えられるか分からないフィールドに付加するキーワードである。

 しかし、実際に実装するとなると話は簡単ではない。1回の操作で書き換えが完了しないような大きなデータは、書き換え中の不完全な姿でほかのスレッドに見えてしまうことがあり得るからだ。それ故に、すべての型にvolatileキーワードを付加することはできない。整数型や実質的に整数(ポインタ)そのものである参照型などにしか付加できない。

 しかし、実質的に「整数として扱えないが本当は整数」として使われるIntPtr型とUIntPtr型に関しては、volatileキーワードの利用を拒む理由はなく、かつ、volatileキーワードが利用できれば有益である。そこで、C# 2.0ではこの2つの型にもvolatileキーワードを付加できることになった。

 ……と簡単に断言できればよいのだが、実はC#の言語仕様Version 1.2を見ると、volatileキーワードを適用できる型は以下のようになっている。

  • reference-type(参照型)
  • byte、sbyte、short、ushort、int、uint、char、float、bool、System.IntPtr、または System.UIntPtr型
  • byte、sbyte、short、ushort、int、またはuintの列挙基本型を持つenum-type(列挙型)

 つまり、IntPtr型とUIntPtr型が含まれている。C# 1.2ですでに用意された機能らしい。

 とはいえ、Visual Studioという製品についての話であれば明確である。Visual Studio .NET 2003のC#コンパイラは、「volatile IntPtr a;」といった記述をエラーとして受け付けない。しかし、Visual Studio 2005のC#コンパイラは受け付ける。そのような意味で、Visual Studioユーザーの観点からはVisual Studio 2005の新機能と受け止めてよい。

インライン警告制御「#pragma warning」

 コンパイラの警告は出ない方がよいと分かっていながら、どうソース・コードを修正しても消せない警告が残ってしまうこともある。かといって、オプション指定で一律に消すのも好ましいことではない。本当のバグの存在に気付くチャンスを逃す可能性もあるためだ。

 C/C++などでは「#pragmaディレクティブ」を埋め込んで、指定した警告をソース・コード中の特定の個所でのみ発生させないように対処することができたが、C# 2.0でもこれと同じことが可能となった。#pragmaディレクティブとそれを用いて警告を制御する「#pragma warning」の機能が用意されたのである。

 書式は難しくはない。以下のような書式で警告の番号を指定するだけである。

#pragma warning disable 警告番号リスト
警告を止めるとき

#pragma warning restore 警告番号リスト
警告を再開させるとき

 ちなみに、警告番号リストを指定しない場合はすべての警告が対象となる。

 これにより、「#pragma warning disable」を記述した行以降では、指定番号の警告は出力されなくなる。そして、「#pragma warning restore」を記述した行以降で、再び警告は出力されるようになる。

 以下は、実際にこれを使用した例である。183番の警告は、「a is int」のような判定式が「コンパイル時に結果を確定できる」場合に発生する。

using System;
using System.Collections.Generic;
using System.Text;

class Program
{
  static void Main(string[] args)
  {
    int a=0;

// 183番の警告を抑止する
#pragma warning disable 183

    if (a is int) // 警告は出ない
    {
    }

// 183番の警告を再開する
#pragma warning restore 183

    if (a is int) // 警告  式は常に指定された型 ('int') です。
    {
    }
  }
}
リスト2 「#pragma warning」により警告を抑止/再開した例


 INDEX
  C# 2.0入門
  最終回 小さな改善とコンパイラの新機能、そして3.0への展望
  1.固定サイズ・バッファ/volatileキーワード/#pragma warning
    2.C#コンパイラの新機能
    3.C# 1.xから2.0への進化とは/C# 2.0から3.0への展望
 
インデックス・ページヘ  「C# 2.0入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間