はじめに 「スネークケース変換」は“別世界の命名ルールをつなぐアダプタ”
C# の世界では UserName や OrderId のような PascalCase/camelCase が主流ですが、
業務で触るのは C# の世界だけじゃないですよね。
データベースのカラム名は user_name。
外部の REST API は order_id。
設定ファイル(YAML, JSON)は access_token。
こういう「snake_case(スネークケース)」と C# の命名ルールの間を、
きれいに変換してくれる小さなユーティリティを持っておくと、
コードが一気に読みやすく、バグも減ります。
ここでは、初心者向けに、
PascalCase/camelCase → snake_case の変換ルール
実務で使える変換ユーティリティの実装
略語(ID, URL など)をどう扱うか
null や変な入力への安全な対応
を、例題付きでかみ砕いて説明していきます。
スネークケースのルールを言葉で整理する
まずは「snake_case とは何か」を言葉で定義しておきます。
すべて小文字で書く。
単語の区切りはアンダースコア _ でつなぐ。
例としては、
user_nameorder_idcreated_at
などです。
C# の UserName や userName と違って、「大文字で区切る」のではなく「アンダースコアで区切る」スタイルです。
この「単語の境目をどう見つけるか」が、変換ロジックの肝になります。
PascalCase/camelCase → snake_case の基本アルゴリズム
ざっくりした考え方
UserName や userName を user_name にしたいとき、
やりたいことはこうです。
文字列を左から 1 文字ずつ見る。
「小文字 → 大文字」に切り替わるところを「単語の境目」とみなす。
境目の前に _ を挟む。
最後に全部小文字にする。
例えば UserName なら、
U ser N ame → user_name
というイメージです。
シンプル実装(まずは分かりやすさ優先)
using System;
using System.Text;
public static class SnakeCaseConverter
{
public static string ToSnakeCase(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var sb = new StringBuilder();
char[] chars = value.Trim().ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
char c = chars[i];
bool isUpper = char.IsUpper(c);
if (i > 0 && isUpper)
{
char prev = chars[i - 1];
bool prevIsLower = char.IsLower(prev);
bool prevIsDigit = char.IsDigit(prev);
if (prevIsLower || prevIsDigit)
{
sb.Append('_');
}
}
sb.Append(char.ToLowerInvariant(c));
}
return sb.ToString();
}
}
C#ここでは、次のようなルールで _ を挿入しています。
先頭文字には _ を付けない。
現在の文字が大文字で、直前が「小文字」または「数字」のときだけ _ を挟む。
これで、UserName → user_name、Order1Number → order1_number のような変換ができます。
動作確認とパターン別の挙動
代表的な例を試してみる
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("UserName")); // user_name
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("userName")); // user_name
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("OrderID")); // order_id
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("CreatedAt")); // created_at
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("URL")); // url
Console.WriteLine(SnakeCaseConverter.ToSnakeCase("User1Name")); // user1_name
Console.WriteLine(SnakeCaseConverter.ToSnakeCase(" UserName ")); // user_name
Console.WriteLine(SnakeCaseConverter.ToSnakeCase(null)); // 空文字
C#ここでの重要ポイントは、
null や空白だけの文字列は、例外にせず空文字にそろえている。
前後の空白は Trim() で落としてから処理している。URL のような「全部大文字」の場合は、そのまま url になる(途中に _ は入らない)。
「全部大文字の略語は 1 単語として扱う」という挙動は、
実務的にもかなり自然で扱いやすいです。
略語(ID, URL, API など)をどう扱うか
「ID」を id にするか i_d にするか問題
略語が混ざると、少し悩ましいケースが出てきます。
例えば UserID をどう変換するか。
今の実装だと、
UserID → user_id
になります。
これは、「User と ID の間に境目がある」とみなして _ を挟んでいるからです。
多くのシステムでは、この挙動で十分自然です。
一方で、もし UId のような変な名前が来た場合、
UId → u_id
となりますが、そもそも UId という命名自体が微妙なので、
そこまでケアするより「ちゃんと PascalCase で書こう」とチームで決めたほうが健全です。
「URLPath」などの複合略語
URLPath のような名前は、今の実装だとこうなります。
URLPath → url_path
URL 全体が 1 単語として扱われ、Path との境目に _ が入る形です。
これも、多くのケースでは自然な結果です。
もし、「URLPath を urlpath にしたい」といった特殊な要件があるなら、
それは個別の例外ルールとして別途処理する必要がありますが、
汎用ユーティリティとしては、今の挙動で十分実用的です。
もう少し厳密にやりたい場合の改良ポイント
「大文字が連続するブロック」を 1 単語とみなす
より厳密にやるなら、「大文字が連続するブロック」を 1 単語とみなす、という考え方もあります。
例えば、
HTTPRequest → http_requestXMLHttpRequest → xml_http_request
のようにしたい場合です。
その場合は、「現在の文字が大文字で、次の文字が小文字なら、そこで単語が終わる」といったルールを追加していきます。
ただし、ロジックが一気に複雑になるので、初心者向け・実務向けの最初の一歩としては、
先ほどのシンプル版で十分です。
「困るケースが具体的に出てきたら、そのときに改良する」で大丈夫です。
実務での使いどころと設計のポイント
どこで snake_case に変換するか
スネークケース変換も、「どの層でやるか」を決めておくとスッキリします。
外部システムが snake_case を要求してくる場合。
内部では PascalCase/camelCase で持っておき、
API や SQL を組み立てる直前で snake_case に変換する。
逆に、外部から snake_case が来る場合。
受け取ったときに camelCase/PascalCase に変換してから内部で扱う(これは前回のキャメルケース変換側の話)。
例えば、Dapper などで SQL を組み立てるときに、
C# のプロパティ名からカラム名を作るユーティリティとして使うイメージです。
string propertyName = "UserName";
string columnName = SnakeCaseConverter.ToSnakeCase(propertyName); // user_name
string sql = $"SELECT {columnName} FROM users";
C#こうしておくと、「C# 側は C# の命名ルール」「DB 側は DB の命名ルール」を守りつつ、
その橋渡しをユーティリティで吸収できます。
まとめ 「スネークケース変換ユーティリティ」は“命名文化の違いを吸収する通訳”
snake_case 変換は、単なる文字列遊びではなく、
「C# と DB/外部API の命名ルールの違いを吸収する通訳」
だと考えると、役割がはっきり見えてきます。
押さえておきたいポイントはこうです。
PascalCase/camelCase → snake_case は、「大文字の前に _ を挟んで、最後に全部小文字にする」という流れで実装できる。
null や空白だけの入力は、例外にせず空文字にそろえると、呼び出し側が楽になる。
略語(ID, URL など)は、「全部大文字のブロックは 1 単語」とみなすシンプルなルールで、実務的には十分なことが多い。
「どの層で snake_case に変換するか」(DB 用、API 用など)を決めておくと、システム全体の命名がきれいに保てる。
ここまで理解できれば、「手で user_name と書いている」段階から一歩進んで、
「C# の名前から自動で snake_case を生成する」実務的なユーティリティを、自分で設計・実装できるようになっていきます。
