C#には、継承を使用したりソースコードを直接修正したりすることなく、既存の型(クラスや構造体、インターフェース)に新しいメソッドを追加できる「拡張メソッド」という機能があります。
これにより、.NET標準のクラス(stringやList<T>など)や、サードパーティ製のライブラリに対して、あたかも元からそのメソッドが存在していたかのように振る舞うユーティリティ機能を実装できます。今回は、string型に対して「タイトルケース(単語の先頭を大文字)への変換」と「Base64エンコード・デコード」機能を追加する拡張メソッドの実装例を紹介します。
拡張メソッドの定義ルール
拡張メソッドを定義するには、以下の3つの条件を満たす必要があります。
- 静的クラス(static class) の中に定義すること。
- メソッド自体も 静的メソッド(static method) であること。
- 第1引数に
thisキーワード を付け、拡張したい型を指定すること。
実践的なコード例:文字列操作の拡張
以下のコードは、文字列(string)型に対して3つのメソッドを拡張する例です。
ToTitleCase: 文字列をタイトルケース(例: “john doe” → “John Doe”)に変換します。ToBase64: 文字列をBase64形式にエンコードします。FromBase64: Base64形式の文字列をもとの文字列にデコードします。
using System;
using System.Globalization;
using System.Text;
using System.Threading;
namespace CustomExtensions
{
class Program
{
static void Main()
{
// --- 1. タイトルケース変換の利用例 ---
string authorName = "arthur conan doyle";
// string型にメソッドが追加されたかのように呼び出せます
Console.WriteLine($"Original : {authorName}");
Console.WriteLine($"Converted: {authorName.ToTitleCase()}");
Console.WriteLine();
// --- 2. Base64エンコード・デコードの利用例 ---
string secretMessage = "API_Key_12345";
// エンコード
string encoded = secretMessage.ToBase64();
Console.WriteLine($"Base64 : {encoded}");
// デコード
string decoded = encoded.FromBase64();
Console.WriteLine($"Decoded : {decoded}");
}
}
// 拡張メソッドを定義する静的クラス
// クラス名は任意ですが、「型名 + Extensions」とするのが一般的です。
public static class StringExtensions
{
/// <summary>
/// 文字列をタイトルケース(各単語の先頭を大文字)に変換します。
/// </summary>
public static string ToTitleCase(this string str)
{
if (string.IsNullOrEmpty(str)) return str;
// 現在のカルチャ情報を使用して変換を行います
CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
TextInfo textInfo = cultureInfo.TextInfo;
return textInfo.ToTitleCase(str);
}
/// <summary>
/// 文字列をBase64形式にエンコードします。
/// </summary>
/// <param name="str">対象の文字列</param>
/// <param name="encoding">エンコーディング(指定がない場合はUTF-8)</param>
public static string ToBase64(this string str, Encoding encoding = null)
{
if (string.IsNullOrEmpty(str)) return string.Empty;
// デフォルトはUTF-8とします(Web等の標準的な利用を想定)
var targetEncoding = encoding ?? Encoding.UTF8;
var bytes = targetEncoding.GetBytes(str);
return Convert.ToBase64String(bytes);
}
/// <summary>
/// Base64形式の文字列をデコードします。
/// </summary>
/// <param name="base64String">Base64文字列</param>
/// <param name="encoding">エンコーディング(指定がない場合はUTF-8)</param>
public static string FromBase64(this string base64String, Encoding encoding = null)
{
if (string.IsNullOrEmpty(base64String)) return string.Empty;
var targetEncoding = encoding ?? Encoding.UTF8;
// Base64文字列をバイト配列に戻し、指定されたエンコーディングで文字列化します
var bytes = Convert.FromBase64String(base64String);
return targetEncoding.GetString(bytes);
}
}
}
実行結果
Original : arthur conan doyle
Converted: Arthur Conan Doyle
Base64 : QVBJX0tleV8xMjM0NQ==
Decoded : API_Key_12345
技術的なポイントと注意点
1. this キーワードの役割
拡張メソッドの第1引数にある this string str は、「このメソッドが string 型のインスタンスに対して呼び出される」ことをコンパイラに伝えます。 メソッド内部では、str 変数を通じて呼び出し元のインスタンス(レシーバ)にアクセスできます。
2. 名前空間(Namespace)の解決
拡張メソッドが定義されているクラス(ここでは StringExtensions)を含む名前空間(CustomExtensions)が、利用側のコードで using されていないと、拡張メソッドは認識されません。IntelliSense(入力補完)に出てこない場合は、名前空間のインポート漏れを確認してください。
3. nullチェックの重要性
通常のインスタンスメソッドであれば、インスタンスが null の場合にメソッドを呼ぶと NullReferenceException が発生します。しかし、拡張メソッドはあくまで「静的メソッド呼び出しのシンタックスシュガー」であるため、レシーバ(第1引数)がnullであってもメソッド内に入ることができます。 そのため、拡張メソッドの冒頭で if (str == null) のようなチェックを行うか、あるいは null でも安全に動作するように実装することが推奨されます。
まとめ
拡張メソッドは、コードの可読性を向上させ、流れるようなインターフェース(Fluent Interface)を構築するのに非常に役立ちます。ただし、乱用すると「どのメソッドがどこに定義されているか」が分かりにくくなるため、汎用的なユーティリティ機能に限定して利用することをお勧めします。
