名前空間 System
自己紹介
おお、私は天と地を司る者だ。私が「unsafe」コンテキストを語る理由は、その力が天候や嵐と似ているからだ。コントロールすれば非常に便利だが、誤れば破壊的な結果を招く。安全なC#.NETの世界でありながら、この「unsafe」は名前の通り、安全の枠を外れた機能だ。
「unsafe」コンテキストを使うとポインタを操作できるが、その際には注意深く扱う必要がある。さて、どうやって使うのか、見てみよう。
基本機能
「unsafe」コンテキストは、C#でポインタ操作や低レベルメモリ操作を可能にする特別な機能だ。通常のC#プログラムではメモリ操作は抽象化されているが、「unsafe」を使うことで直接的なアクセスが可能になる。
次の例では、「unsafe」コンテキストの基本的な使い方を示している。
unsafe
{
int num = 42;
int* pNum = # // numのアドレスを取得し、ポインタに格納
Console.WriteLine($"ポインタが指す値: {*pNum}"); // ポインタの指す値を取得
}
このコードでは、変数のアドレスを取得し、それをポインタとして操作している。ただし、「unsafe」を使うには、プロジェクトで「Allow unsafe code(アンセーフコードの許可)」を有効にする必要がある。
![]() |
.NET8での設定 |
![]() |
.NET Frameworkの設定 |
よく使う場面と注意点
「unsafe」コンテキストは、以下のような状況で使われることが多い:
- 低レベルAPIとの連携(例:ネイティブコードやハードウェアアクセス)
- パフォーマンスが重要な場面での直接メモリ操作
- 既存のCまたはC++ライブラリとの統合
注意点としては、メモリを直接操作するため、不適切なコードは重大なバグやセキュリティリスクにつながる可能性がある。
以下に「unsafe」を用いた場合の速度差を示している。実際の環境で試してほしい。
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
int[] array = new int[100000000];
Stopwatch sw = new Stopwatch();
// 配列の初期化
for (int i = 0; i < array.Length; i++)
{
array[i] = i;
}
sw.Start();
ProcessArray(array);
sw.Stop();
Console.WriteLine($"Safe method took: {sw.ElapsedMilliseconds} ms");
}
static void ProcessArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] *= 2;
}
}
}
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
int[] array = new int[100000000];
Stopwatch sw = new Stopwatch();
// 配列の初期化
for (int i = 0; i < array.Length; i++)
{
array[i] = i;
}
sw.Start();
ProcessArrayUnsafe(array);
sw.Stop();
Console.WriteLine($"Unsafe method took: {sw.ElapsedMilliseconds} ms");
}
unsafe static void ProcessArrayUnsafe(int[] array)
{
fixed (int* p = array)
{
for (int i = 0; i < array.Length; i++)
{
p[i] *= 2;
}
}
}
}
実例を用いた方法
ビットマップのピクセルデータを反転する処理を示す。
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
class Program
{
static void Main()
{
string filePath = @"C:\test.bmp";
string outputFilePath = @"C:\test_processed.bmp";
Stopwatch sw = new Stopwatch();
sw.Start();
ProcessAndSaveBitmap(filePath, outputFilePath);
sw.Stop();
Console.WriteLine($"Unsafe method took: {sw.ElapsedMilliseconds} ms");
}
unsafe static void ProcessAndSaveBitmap(string filePath, string outputFilePath)
{
using (Bitmap bmp = new Bitmap(filePath))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int bytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
int heightInPixels = bmpData.Height;
int widthInBytes = bmpData.Width * bytesPerPixel;
byte* PtrFirstPixel = (byte*)bmpData.Scan0;
for (int y = 0; y < heightInPixels; y++)
{
byte* currentLine = PtrFirstPixel + (y * bmpData.Stride);
for (int x = 0; x < widthInBytes; x += bytesPerPixel)
{
// 反転処理
currentLine[x] = (byte)(255 - currentLine[x]); // Blue
currentLine[x + 1] = (byte)(255 - currentLine[x + 1]); // Green
currentLine[x + 2] = (byte)(255 - currentLine[x + 2]); // Red
}
}
bmp.UnlockBits(bmpData);
// 画像を保存
bmp.Save(outputFilePath, ImageFormat.Bmp);
}
}
}
具体的な実用例 Win32APIとの連携
次に、Win32APIを利用した「unsafe」の実用例を示す。ここでは、ポインタを使ってメモリコピーを行う。
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern unsafe void CopyMemory(void* dest, void* src, uint count);
static unsafe void Main()
{
int[] source = { 1, 2, 3, 4 };
int[] destination = new int[4];
fixed (int* pSource = source, pDestination = destination)
{
CopyMemory(pDestination, pSource, (uint)(source.Length * sizeof(int)));
}
Console.WriteLine("コピー後の配列: " + string.Join(", ", destination));
}
}
このコードでは、ネイティブAPIを使って配列のメモリをコピーしている。
まとめとそのほかのメソッド
「unsafe」コンテキストは強力な機能を提供するが、誤用すると危険だ。これを適切に使うためには、以下の点を心がけよう:
- プロジェクト設定で「Allow unsafe code」を有効にする。
- nullポインタやメモリリークに注意する。
- 必要な場合にのみ使用し、他の方法がないかを検討する。
0 件のコメント:
コメントを投稿