.NET ヘルプ

C# スパン(開発者のための仕組み)

Spanは、C# 7.2でSystem名前空間のSpan<T>構造体の一部として導入された型です。 これは任意のメモリの連続した領域を表すように設計されています。 配列や管理ヒープのようなコレクションとは異なり、Span はそれが指すスタックメモリやメモリ領域を所有しません。 代わりに、既存のメモリブロックに対して軽量なビューを提供します。 この特性は、追加のオーバーヘッドや安全でないコードのシナリオを発生させることなく、メモリバッファーを効率的に扱う必要があるシナリオにおいて、Span を特に強力にします。 この記事の後半では、IronPDFライブラリIron Softwareの紹介も見ていきます。

スパンの主要特性

メモリ管理

C#のSpanは、従来のヒープ割り当てに頼ることなくメモリを直接操作することを開発者に許可します。 それは既存の配列や他のメモリソースからメモリのスライスを作成する方法を提供し、追加のメモリコピーを必要としません。

ゼロコピー抽象化

C# Spanの際立った特徴の一つは、ゼロコピー抽象化です。 データを重複させるのではなく、Spanは既存のメモリを効率的に参照する方法を提供します。 これは、大量のデータをコピーすることが不可能またはコストがかかりすぎるシナリオにおいて特に有益です。

ポインタのような操作

C#は伝統的に高水準で安全な言語でしたが、SpanはCやC++のような言語でポインターを操作するのと同様に、低レベルのメモリ操作の要素を導入します。 開発者は、C#の安全で管理された特徴を犠牲にすることなく、ポインタのような操作を実行することができます。

4. 不変の性質

その低レベルのメモリアクセスに対する能力にもかかわらず、C# Spanは不変です。 これは、メモリの操作を許可する一方で、意図しない変更を防ぐことによって安全性を確保することを意味します。

using System;
class Program
{
    void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a span that points to the entire array
        Span<int> span = array;
        // Modify the data using the span
        span[2] = 10;
        // Print the modified array
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
    }
}
using System;
class Program
{
    void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a span that points to the entire array
        Span<int> span = array;
        // Modify the data using the span
        span[2] = 10;
        // Print the modified array
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
    }
}
Imports System
Friend Class Program
	Private Sub Main()
		Dim array() As Integer = { 1, 2, 3, 4, 5 }
		' Create a span that points to the entire array
		Dim span As Span(Of Integer) = array
		' Modify the data using the span
		span(2) = 10
		' Print the modified array
		For Each item In array
			Console.WriteLine(item)
		Next item
	End Sub
End Class
$vbLabelText   $csharpLabel

"ReadOnlySpan"

Spanは可変であり、基になるデータの変更を許可しますが、ReadOnlySpanはメモリの不変のビューです。 それは隣接するメモリ領域への読み取り専用インターフェースを提供し、データを変更せずに読み取るだけでよい場合に適しています。

以下は重要なポイントです。

1. 読み取り専用ビュー

その名の通り、ReadOnlySpan<T>はメモリブロックの読み取り専用ビューを作成することを可能にします。 これは、ReadOnlySpanを通じて要素を変更できないことを意味します。

メモリ表現

Span 同様、ReadOnlySpan はそれが指すメモリを所有しません。 配列やスタックに割り当てられたメモリ、あるいはネイティブメモリを指す既存のメモリを指します。

パフォーマンスの利点

Span<T>と同様に、ReadOnlySpan<T>は、特に大量のデータを扱う際に、コピーの必要性を減らすことで、従来のコレクションタイプと比べてより良いパフォーマンスを提供することができます。

範囲チェックなし

Spanと同様に、ReadOnlySpanは境界チェックを行いません。 操作が基盤となるメモリの範囲内に収まるようにすることは、開発者の責任です。

5. 配列スライシングでの使用

ReadOnlySpanはスライスをサポートしており、元のメモリの一部を参照するサブスパンを作成することができます。

using System;
class Program
{
    static void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a read-only span that points to the entire array
        ReadOnlySpan<int> readOnlySpan = array;
        // Access and print the data through the read-only span
        foreach (var item in readOnlySpan)
        {
            Console.WriteLine(item);
        }
        // Note: The following line would result in a compilation error since readOnlySpan is read-only.
        // readOnlySpan[2] = 10;
    }
}
using System;
class Program
{
    static void Main()
    {
        int[] array = { 1, 2, 3, 4, 5 };
        // Create a read-only span that points to the entire array
        ReadOnlySpan<int> readOnlySpan = array;
        // Access and print the data through the read-only span
        foreach (var item in readOnlySpan)
        {
            Console.WriteLine(item);
        }
        // Note: The following line would result in a compilation error since readOnlySpan is read-only.
        // readOnlySpan[2] = 10;
    }
}
Imports System
Friend Class Program
	Shared Sub Main()
		Dim array() As Integer = { 1, 2, 3, 4, 5 }
		' Create a read-only span that points to the entire array
		Dim readOnlySpan As ReadOnlySpan(Of Integer) = array
		' Access and print the data through the read-only span
		For Each item In readOnlySpan
			Console.WriteLine(item)
		Next item
		' Note: The following line would result in a compilation error since readOnlySpan is read-only.
		' readOnlySpan[2] = 10;
	End Sub
End Class
$vbLabelText   $csharpLabel

ReadOnlySpan を作成して使用する方法は多くあります。 以下はいくつかの例です。

1. 文字列からReadOnlySpanを作成する

string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
string msg = "Hello, World!";
ReadOnlySpan<char> span1 = msg.AsSpan();
// Read-only manipulation
char firstChar = span1[0];
Console.WriteLine(firstChar); // Outputs: H
Dim msg As String = "Hello, World!"
Dim span1 As ReadOnlySpan(Of Char) = msg.AsSpan()
' Read-only manipulation
Dim firstChar As Char = span1(0)
Console.WriteLine(firstChar) ' Outputs: H
$vbLabelText   $csharpLabel

サブストリングの操作

ReadOnlySpan に対して Slice を使用する

ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
ReadOnlySpan<char> substringSpan = spanFromString.Slice(startIndex, length);
Dim substringSpan As ReadOnlySpan(Of Char) = spanFromString.Slice(startIndex, length)
$vbLabelText   $csharpLabel

メソッドに部分文字列を渡す

ReadOnlySpanをパラメーターとしてメソッドに渡します。

void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    // Perform operations on the substring
}
// Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
void ProcessSubstringfromReadOnlySpan(ReadOnlySpan<char> substring)
{
    // Perform operations on the substring
}
// Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length));
Private Sub ProcessSubstringfromReadOnlySpan(ByVal substring As ReadOnlySpan(Of Char))
	' Perform operations on the substring
End Sub
' Usage
ProcessSubstringfromReadOnlySpan(spanFromString.Slice(startIndex, length))
$vbLabelText   $csharpLabel

文字列内の検索

IndexOf()で文字列内を検索するためのReadOnlySpan

int index = stringSpan.IndexOf('W');
int index = stringSpan.IndexOf('W');
Dim index As Integer = stringSpan.IndexOf("W"c)
$vbLabelText   $csharpLabel

メモリマップトファイルの使用

ReadOnlySpanは、メモリマップドファイルでより効率的です。

using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
    using (var accessor = memmf.CreateViewAccessor())
    {
        ReadOnlySpan<byte> dataSpan;
        accessor.Read(0, out dataSpan);
        // Process data directly from the memory-mapped file
        ProcessData(dataSpan);
    }
}
using (var memmf = MemoryMappedFile.CreateFromFile("data.bin"))
{
    using (var accessor = memmf.CreateViewAccessor())
    {
        ReadOnlySpan<byte> dataSpan;
        accessor.Read(0, out dataSpan);
        // Process data directly from the memory-mapped file
        ProcessData(dataSpan);
    }
}
Using memmf = MemoryMappedFile.CreateFromFile("data.bin")
	Using accessor = memmf.CreateViewAccessor()
		Dim dataSpan As ReadOnlySpan(Of Byte) = Nothing
		accessor.Read(0, dataSpan)
		' Process data directly from the memory-mapped file
		ProcessData(dataSpan)
	End Using
End Using
$vbLabelText   $csharpLabel

6. 効率的な文字列操作

ReadOnlySpan<char>は、効率的な文字列操作に使用できます。

// Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
// Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan);
' Replace a character in a substring without creating a new string
spanFromString.Slice(startIndex, length).CopyTo(newSpan)
$vbLabelText   $csharpLabel

7. APIに部分文字列を渡す

文字範囲を操作する外部ライブラリやAPIを使用する際。

void ExternalApiMethod(ReadOnlySpan<char> data)
{
    // Call the external API with the character span
}
// Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length));
void ExternalApiMethod(ReadOnlySpan<char> data)
{
    // Call the external API with the character span
}
// Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length));
Private Sub ExternalApiMethod(ByVal data As ReadOnlySpan(Of Char))
	' Call the external API with the character span
End Sub
' Usage
ExternalApiMethod(spanFromString.Slice(startIndex, length))
$vbLabelText   $csharpLabel

ReadOnlySpanは、メモリ割り当てやコピーを最小限に抑える必要があるシナリオにおいて、より効率的に文字列を操作する方法を提供します。 これは、パフォーマンスが重要なコードを最適化するための強力なツールであり、大量の文字列データを扱う際に特に有益です。

スパンの制限

C#のSpanは多くの利点を持つ強力な機能ですが、特に連続メモリと非連続メモリの文脈において、いくつかの制限や考慮事項が伴います。 これらの制限を見てみましょう。

連続メモリバッファ

自動メモリ管理なし

Spanは、それが指しているメモリを管理しません。 これは、基礎となる管理されたメモリが解放されるかスコープ外に出た場合、Spanを使用すると不定な動作や潜在的なクラッシュを引き起こす可能性があることを意味します。 開発者は、Spanを使用する際に基盤となるメモリが依然として有効であることを確認する必要があります。

1.2 ガベージコレクションなし

Spanはメモリを保持しないため、ガベージコレクションの対象にはなりません。 したがって、Span自体よりも短い寿命を持つスタックに割り当てられたメモリやメモリを扱うときは注意が必要です。

境界チェックは無効です

Span と ReadOnlySpan はデフォルトで境界チェックを行いません。 これは慎重に使用しないと無効なメモリ位置にアクセスする可能性があります。 開発者は、Span<T> に対する操作が基になるメモリの範囲内にとどまることを手動で保証する必要があります。

非連続メモリのサポートなし

Spanは連続したメモリで動作するように設計されています。 非連続メモリがある場合や、より複雑なデータ構造を表現する必要がある場合、Span は最も適切な選択肢ではないかもしれません。

すべての操作がサポートされるわけではありません

Span はスライス、インデックス、イテレーションのような多くの一般的な操作をサポートしていますが、すべての操作がサポートされているわけではありません。 例えば、Spanをリサイズすることはできず、基になるメモリの長さを変更する操作は許可されていません。

1.6 限定プラットフォーム互換性

Span は .NET Standard と .NET Core の一部ですが、すべてのプラットフォームや環境で利用可能とは限りません。 コードで使用する予定がある場合は、ターゲットプラットフォームがSpanをサポートしていることを確認することが重要です。

2. 非連続メモリバッファ

2.1 非連続メモリの限定サポート

ReadOnlySpan は、主に連続するメモリブロックまたはバッファーとスムーズに連携するために設計されています。 非連続のメモリバッファやメモリにギャップのある構造が関与するシナリオでは、最適な選択肢ではないかもしれません。

構造的制限 2.2

非連続メモリに依存する特定のデータ構造やシナリオは、ReadOnlySpanとよく適合しない可能性があります。 たとえば、ReadOnlySpan の連続メモリ要件のため、連結リストやグラフ構造のようなデータ構造は適していないかもしれません。

2.3 複雑なポインタ操作

非連続メモリを含む状況、特に複雑なポインター演算が必要な状況では、ReadOnlySpan は C++ のような言語の生ポインターのような低レベルの制御と柔軟性を提供しないかもしれません。 そのような場合、ポインターを使用するアンセーフコードを利用する方が適切である可能性があります。

2.4 一部のAPIにおける直接サポートの欠如

連続メモリに似て、ReadOnlySpanで表される非連続メモリを直接サポートするAPIやライブラリがすべてではないことに注意することが重要です。 このようなシナリオに適応するには、互換性を確保するために追加の中間ステップや変換が必要になるかもしれません。

Span および Unmanaged Memory

C#では、Spanを使用すると、管理されていないメモリに対して制御された効率的な方法でメモリ関連の操作を実行できます。 アンマネージメモリとは、.NETランタイムのガベージコレクタによって管理されていないメモリのことを指し、ネイティブメモリの割り当てや解放を伴うことが多いです。 ここでは、C#でアンマネージメモリを使用するためにSpanを活用する方法を説明します。

非管理メモリの割り当て

アンマネージドメモリを割り当てるには、System.Runtime.InteropServices.MemoryMarshal クラスを使用できます。 Marshal.AllocHGlobal メソッドはメモリを割り当て、割り当てられたブロックへのポインターを返します。 割り当てられたメモリまたはメモリアドレスは、unmanagedMemory ポインタに保持され、読み書きアクセスが可能です。 連続したメモリ領域は簡単にアクセスできます。

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use the Span as needed...
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use the Span as needed...
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		Const bufferSize As Integer = 100
		Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
		' Create a Span from the unmanaged memory
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		' Use the Span as needed...
		' Don't forget to free the unmanaged memory when done
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
$vbLabelText   $csharpLabel

上記のソースコードでは、Marshal.AllocHGlobal を使用して非管理メモリのブロックを割り当て、その後、非管理メモリから取得したポインタを使用して Span を作成します。 これにより、慣れ親しんだSpan APIを使用して、非管理メモリを操作できるようになります。 アンマネージドメモリを使用するときは、メモリの割り当てと割り当て解除の管理が自分の責任であることに注意することが重要です。

アンマネージドメモリへのデータのコピーおよびアンマネージドメモリからのデータのコピー

スパンは Slice、CopyTo、ToArray などのメソッドを提供し、管理メモリと非管理メモリ間でデータを効率的にコピーするために使用できます。

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        // Managed array to copy data from
        int[] sourceArray = { 1, 2, 3, 4, 5 };
        // Allocate unmanaged memory for the destination data
        IntPtr destinationPointer = MemoryMarshal.Allocate<int>(sourceArray.Length);
        try
        {
            // Create a Span<int> from the source array
            Span<int> sourceSpan = sourceArray;
            // Create a Span<int> from the allocated unmanaged memory
            Span<int> destinationSpan = MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length);
            // Copy data from the source Span<int> to the destination Span<int>
            sourceSpan.CopyTo(destinationSpan);
            // Print the values in the destination memory
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            // Deallocate the unmanaged memory when done
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        // Managed array to copy data from
        int[] sourceArray = { 1, 2, 3, 4, 5 };
        // Allocate unmanaged memory for the destination data
        IntPtr destinationPointer = MemoryMarshal.Allocate<int>(sourceArray.Length);
        try
        {
            // Create a Span<int> from the source array
            Span<int> sourceSpan = sourceArray;
            // Create a Span<int> from the allocated unmanaged memory
            Span<int> destinationSpan = MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length);
            // Copy data from the source Span<int> to the destination Span<int>
            sourceSpan.CopyTo(destinationSpan);
            // Print the values in the destination memory
            Console.WriteLine("Values in the destination memory:");
            foreach (var value in destinationSpan)
            {
                Console.Write($"{value} ");
            }
        }
        finally
        {
            // Deallocate the unmanaged memory when done
            MemoryMarshal.Free(destinationPointer);
        }
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		' Managed array to copy data from
		Dim sourceArray() As Integer = { 1, 2, 3, 4, 5 }
		' Allocate unmanaged memory for the destination data
		Dim destinationPointer As IntPtr = MemoryMarshal.Allocate(Of Integer)(sourceArray.Length)
		Try
			' Create a Span<int> from the source array
			Dim sourceSpan As Span(Of Integer) = sourceArray
			' Create a Span<int> from the allocated unmanaged memory
			Dim destinationSpan As Span(Of Integer) = MemoryMarshal.Cast(Of Integer, Byte)(destinationPointer, sourceArray.Length)
			' Copy data from the source Span<int> to the destination Span<int>
			sourceSpan.CopyTo(destinationSpan)
			' Print the values in the destination memory
			Console.WriteLine("Values in the destination memory:")
			For Each value In destinationSpan
				Console.Write($"{value} ")
			Next value
		Finally
			' Deallocate the unmanaged memory when done
			MemoryMarshal.Free(destinationPointer)
		End Try
	End Sub
End Class
$vbLabelText   $csharpLabel

この例では:

MemoryMarshal.Allocate(sourceArray.Length) は、宛先データのためにアンマネージドメモリを割り当てます。 MemoryMarshal.Cast<int, byte>(destinationPointer, sourceArray.Length) は、割り当てられた非管理メモリから Span を作成します。 sourceSpan.CopyTo(destinationSpan) メソッドは、管理された配列から非管理メモリへデータをコピーします。 コピー操作を検証するために、宛先メモリの値が印刷されます。 MemoryMarshal.Free(destinationPointer) メソッドは、処理が終了した際に管理されていないメモリを解放するために使用されます。

アンセーフコードの使用

管理されていないメモリを扱う際には、ポインタを使用したunsafeコードも使用することができます。 このような場合、Unsafe.AsPointer() メソッドを使用して Span からポインターを取得することができます。

using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use unsafe code to work with pointers
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            // Use the pointer as needed...
        }
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
using System;
using System.Runtime.InteropServices;
class Program
{
    static void Main()
    {
        const int bufferSize = 100;
        IntPtr unmanagedMemory = Marshal.AllocHGlobal(bufferSize);
        // Create a Span from the unmanaged memory
        Span<byte> span = new Span<byte>(unmanagedMemory.ToPointer(), bufferSize);
        // Use unsafe code to work with pointers
        // ref t
        unsafe
        {
            byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
            // Use the pointer as needed...
        }
        // Don't forget to free the unmanaged memory when done
        Marshal.FreeHGlobal(unmanagedMemory);
    }
}
Imports System
Imports System.Runtime.InteropServices
Friend Class Program
	Shared Sub Main()
		Const bufferSize As Integer = 100
		Dim unmanagedMemory As IntPtr = Marshal.AllocHGlobal(bufferSize)
		' Create a Span from the unmanaged memory
		Dim span As New Span(Of Byte)(unmanagedMemory.ToPointer(), bufferSize)
		' Use unsafe code to work with pointers
		' ref t
'INSTANT VB TODO TASK: C# 'unsafe' code is not converted by Instant VB:
'		unsafe
'		{
'			byte* pointer = (byte*)Unsafe.AsPointer(ref struct MemoryMarshal.GetReference(span));
'			' Use the pointer as needed...
'		}
		' Don't forget to free the unmanaged memory when done
		Marshal.FreeHGlobal(unmanagedMemory)
	End Sub
End Class
$vbLabelText   $csharpLabel

この例では、Unsafe.AsPointerメソッドを使用してSpanからポインタを取得します。 これは、ポインタを直接操作する際にアンセーフコードを使用できるようにします。

忘れないでください。アンマネージドメモリを使用する際は、メモリリークを避けるために、メモリの割り当てと解放を適切に管理することが重要です。 常に適切な方法を使用して、Marshal.FreeHGlobal() などで管理されないメモリを解放してください。 さらに、アンセーフコードを使用する際には注意を払いましょう。適切に処理されない場合、潜在的なセキュリティリスクを引き起こす可能性があります。

スパンおよび非同期メソッド呼び出し

C#で非同期メソッド呼び出しと共にSpanを使用することは、特に大量のデータやI/O操作を扱う際に強力な組み合わせです。 目標は、データの不必要なコピーを避けつつ、非同期操作を効率的に処理することです。 非同期シナリオでどのようにSpanを活用できるかを探求しましょう:

非同期 I/O 操作:

非同期 I/O 操作、例えばストリームへのデータの読み書きを処理する際には、追加のバッファを作成することなくデータを効率的に扱うために、Memory または Span を使用することができます。

async Task ProcessDataAsync(Stream stream)
{
    const int bufferSize = 4096;
    byte[] buffer = new byte[bufferSize];
    while (true)
    {
        int bytesRead = await stream.ReadAsync(buffer.AsMemory());
        if (bytesRead == 0)
            break;
        // Process the data using Span without unnecessary copying
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
async Task ProcessDataAsync(Stream stream)
{
    const int bufferSize = 4096;
    byte[] buffer = new byte[bufferSize];
    while (true)
    {
        int bytesRead = await stream.ReadAsync(buffer.AsMemory());
        if (bytesRead == 0)
            break;
        // Process the data using Span without unnecessary copying
        ProcessData(buffer.AsSpan(0, bytesRead));
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
Async Function ProcessDataAsync(ByVal stream As Stream) As Task
	Const bufferSize As Integer = 4096
	Dim buffer(bufferSize - 1) As Byte
	Do
		Dim bytesRead As Integer = Await stream.ReadAsync(buffer.AsMemory())
		If bytesRead = 0 Then
			Exit Do
		End If
		' Process the data using Span without unnecessary copying
		ProcessData(buffer.AsSpan(0, bytesRead))
	Loop
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

この例では、ReadAsync メソッドは非同期的にストリームからデータをバッファに読み込みます。 ProcessData メソッドは、その後、データを別のバッファにコピーせずに Span から直接処理します。

2. 非同期ファイル操作:

I/O処理と同様に、非同期ファイル操作を扱う際には、追加のコピーを行わずにデータを効率的に処理するためにSpanを使用できます。

async Task ProcessFileAsync(string filePath)
{
    const int bufferSize = 4096;
    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        byte[] buffer = new byte[bufferSize];
        while (true)
        {
            int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
            if (bytesRead == 0)
                break;
            // Process the data using Span without unnecessary copying
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
async Task ProcessFileAsync(string filePath)
{
    const int bufferSize = 4096;
    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
    {
        byte[] buffer = new byte[bufferSize];
        while (true)
        {
            int bytesRead = await fileStream.ReadAsync(buffer.AsMemory());
            if (bytesRead == 0)
                break;
            // Process the data using Span without unnecessary copying
            ProcessData(buffer.AsSpan(0, bytesRead));
        }
    }
}
void ProcessData(Span<byte> data)
{
    // Perform operations on the data
}
Async Function ProcessFileAsync(ByVal filePath As String) As Task
	Const bufferSize As Integer = 4096
	Using fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)
		Dim buffer(bufferSize - 1) As Byte
		Do
			Dim bytesRead As Integer = Await fileStream.ReadAsync(buffer.AsMemory())
			If bytesRead = 0 Then
				Exit Do
			End If
			' Process the data using Span without unnecessary copying
			ProcessData(buffer.AsSpan(0, bytesRead))
		Loop
	End Using
End Function
Private Sub ProcessData(ByVal data As Span(Of Byte))
	' Perform operations on the data
End Sub
$vbLabelText   $csharpLabel

ここで、ReadAsync メソッドはファイルストリームからバッファにデータを読み込み、ProcessData メソッドは Span から直接データを処理します。

3. 非同期タスク処理:

非同期タスクでデータを生成または消費する場合、不必要なコピーを避けるために、Memory または Span を使用できます。

async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
async Task Main()
{
    int[] inputData = Enumerable.Range(1, 1000).ToArray();
    // Process the data asynchronously without copying
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
async Task<int> ProcessDataAsync(int[] data)
{
    // Asynchronous processing of data
    await Task.Delay(1000);
    // Returning the length of the processed data
    return data.Length;
}
async Task Main()
{
    int[] inputData = Enumerable.Range(1, 1000).ToArray();
    // Process the data asynchronously without copying
    int processedLength = await ProcessDataAsync(inputData.AsMemory());
    Console.WriteLine($"Processed data length: {processedLength}");
}
Async Function ProcessDataAsync(ByVal data() As Integer) As Task(Of Integer)
	' Asynchronous processing of data
	Await Task.Delay(1000)
	' Returning the length of the processed data
	Return data.Length
End Function
Async Function Main() As Task
	Dim inputData() As Integer = Enumerable.Range(1, 1000).ToArray()
	' Process the data asynchronously without copying
	Dim processedLength As Integer = Await ProcessDataAsync(inputData.AsMemory())
	Console.WriteLine($"Processed data length: {processedLength}")
End Function
$vbLabelText   $csharpLabel

この例では、ProcessDataAsync メソッドがデータを非同期に処理し、追加のコピーを必要とせずに処理されたデータの長さを返します。

IronPDFの紹介

IronPDF ライブラリの概要 は、Iron Software からの最新の C# PDF ライブラリであり、C# コードを使用して動的に美しい PDF ドキュメントを即座に生成することができます。 IronPDFは、HTMLからのPDF生成、HTMLコンテンツのPDFへの変換、PDFファイルの結合や分割など、さまざまな機能を提供します。

IronPDFの主な機能は、レイアウトとスタイルを保持するHTML to PDF機能です。 WebコンテンツからPDFを生成することができるので、レポート、請求書、ドキュメントに最適です。 このツールは、HTMLファイル、URL、およびHTML文字列をPDFファイルに変換することをサポートしています。

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

インストール

IronPDFは、NuGetパッケージマネージャーコンソールまたはVisual Studioパッケージマネージャーを使用してインストールできます。

dotnet add package IronPdf
// Or
Install-Package IronPdf
dotnet add package IronPdf
// Or
Install-Package IronPdf
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'dotnet add package IronPdf Install-Package IronPdf
$vbLabelText   $csharpLabel

C# Span(開発者にとっての仕組み):図1 - NuGet パッケージマネージャーの検索バーで「ironpdf」と検索して IronPDF を NuGet パッケージマネージャーを使用してインストールします

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>First Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = @"<!DOCTYPE html>
<html>
<body>".AsSpan();
        var end = @"<!DOCTYPE html>
<html>
<body>";
        var content = string.Concat(start, displayFirstName, displayLastName, string.Concat(displayAddress, end));
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Generating PDF using IronPDF.");
        var displayFirstName = "<p>First Name is Joe</p>".AsSpan();
        var displayLastName = "<p>First Name is Doe</p>".AsSpan();
        var displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan();
        var start = @"<!DOCTYPE html>
<html>
<body>".AsSpan();
        var end = @"<!DOCTYPE html>
<html>
<body>";
        var content = string.Concat(start, displayFirstName, displayLastName, string.Concat(displayAddress, end));
        var pdfDocument = new ChromePdfRenderer();
        pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf");
    }
}
Imports System
Friend Class Program
	Shared Sub Main()
		Console.WriteLine("Generating PDF using IronPDF.")
		Dim displayFirstName = "<p>First Name is Joe</p>".AsSpan()
		Dim displayLastName = "<p>First Name is Doe</p>".AsSpan()
		Dim displayAddress = "<p>12th Main, 7Th Cross, New York</p>".AsSpan()
		Dim start = "<!DOCTYPE html>
<html>
<body>".AsSpan()
		Dim [end] = "<!DOCTYPE html>
<html>
<body>"
		Dim content = String.Concat(start, displayFirstName, displayLastName, String.Concat(displayAddress, [end]))
		Dim pdfDocument = New ChromePdfRenderer()
		pdfDocument.RenderHtmlAsPdf(content).SaveAs("span.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

この例では、SpanIronPDFを使用してPDFドキュメントを生成しています。

出力:

C# スパン(開発者にとっての作動方法):図 2 - コンソール出力

生成されたPDF:

C# Span (開発者がどう利用するか): 図3 - PDF出力

ライセンス(無料トライアル利用可能)

IronPDFライセンス情報。 このキーはappsettings.jsonに配置する必要があります。

"IronPdf.LicenseKey": "your license key"
"IronPdf.LicenseKey": "your license key"
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'"IronPdf.LicenseKey": "your license key"
$vbLabelText   $csharpLabel

試用ライセンスを取得するためにメールアドレスを提供してください。

結論

C#のSpanは、パフォーマンスと柔軟性の観点でメリットを提供する、メモリ操作における強力で効率的な方法を提供します。 その非所有の連続した性質は、メモリ割り当てとコピーを最小限に抑えることが重要なシナリオに特に適しています。 Spanを活用することで、開発者は文字列操作から高性能な数値処理に至るまで、さまざまなアプリケーションにおいて優れたパフォーマンスを実現できます。 その機能を理解し、その制限を考慮することで、開発者はSpanを利用してさまざまなメモリ操作タスクを安全かつ効率的に行うことができます。 IronPDFライブラリ概要とともに、awaitやyieldの境界なしで素晴らしいPDFドキュメントを生成するために使用することができます。

こちらのIronPDFのクイックスタートドキュメントページをご覧ください。

チペゴ
ソフトウェアエンジニア
チペゴは優れた傾聴能力を持ち、それが顧客の問題を理解し、賢明な解決策を提供する助けとなっています。彼は情報技術の学士号を取得後、2023年にIron Softwareチームに加わりました。現在、彼はIronPDFとIronOCRの2つの製品に注力していますが、顧客をサポートする新しい方法を見つけるにつれて、他の製品に関する知識も日々成長しています。Iron Softwareでの協力的な生活を楽しんでおり、さまざまな経験を持つチームメンバーが集まり、効果的で革新的な解決策を提供することに貢献しています。チペゴがデスクを離れているときは、良い本を楽しんだり、サッカーをしていることが多いです。
< 以前
C# IDE(開発者向けの仕組み)
次へ >
Opentelemetry C#(開発者向けの仕組み)