syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

メンバー変数命名規則

(これは2010-10-20に書いた故OCNブログの記事を移植したものです)

自分はC++の場合、classメンバー変数の命名規則として、ずっと m_ プレフィックスを付けるようにしています。
ただし、すべてのメンバーを外部公開するstructメンバー変数(ここでいうstructとはC互換のPOD型となりうるデータ型のことであって、決してclassと同列に扱わないことが前提)の場合は、大文字で始めてPascal形式にしています(例:DisplayOption::MessageWindowOpacity)。こいつはC#のstructのpublicフィールドやらプロパティやらを真似ているものです。ごく短い1、2文字までのものに関しては全て小文字とすることもあります(例:Vector3F::x)。

class GamePlayer
{
private:
    std::string m_userName;
    uint64_t m_birthday;
public:
    explicit GamePlayer(const std::string& userName, uint64_t birthday)
    : m_userName(userName), m_birthday(birthday)
    {}
};
// "m_" の後は大文字ではなく小文字なのがマイルール。

struct DisplayOption
{
    float MessageWindowOpacity;
    bool IsFullScreenMode;
};

struct Vector3F
{
    float x, y, z;
};

enum MyAttribute
{
    MyAttribute_None,
    MyAttribute_Earth,
    MyAttribute_Air,
    MyAttribute_Fire,
    MyAttribute_Water,
    MyAttribute_AllCount
};
// 従来の enum ではスコープ解決演算子 "::" が使えない代わりに "_" で区切るのがマイルール。

// C++11 (C++0x)
enum class MyElement
{
    Gnome,
    Sylph,
    Salamander,
    Ondine,
};

メンバーのプレフィックスあるいはポストフィックスにアンダースコアを使う人もいるんですが、C/C++においてアンダースコアで始まるシンボルは処理系のために予約されているシンボルということになっているのでプログラマーが使うのはNGです*1。かといってポストフィックスにすると、インテリセンス(コード補完)の恩恵を受けられなくなるので、これも却下です*2。だいたいシンボル名の最後まで読まないとメンバーか否かというスコープ情報を判断できないなんてアホだと思います。要するに生理的に受け付けません(C++ Coding Standardsは名著だが、サンプルコードがポストアンダースコアなのが好きではない)。

ただ、classのメンバー変数はpublicやprotectedではなくprivateにするのが正しいカプセル化の原則なので、そのclassを保守する人はともかくとして、classのユーザーにその名前が見えてしまうことはあまりないです(というかあってはならない)。なので、ある意味分かりやすく保守しやすい命名規則に従ってさえいればいいと言えます。とはいえ、デバッガーでメンバー変数のツリーを閲覧しているときなどに、統一された命名規則で実装されていたほうが気持ちがいいのは事実ですが……

C#の場合

ちなみに自分はC#でも、基本的にはMSの.NET命名規則に従っているけれど、classの非公開フィールドに関しては f_ もしくは _ で始めることがあります。というのも、"f_" もしくは "_" と入力するだけですぐに候補が表示されるようになるというメリットがありますし、ぱっと見ローカル変数と区別が付く命名規則にしとかないとバグの原因になりかねないので。ちなみにC#ではC/C++と違ってアンダースコアで始まるシンボル名は処理系予約ではなく、自由に使ってよいので、f_ でなく _ で始めてもOKです。*3

例えば、下記のような例では、フィールドを変更するつもりだったのにローカル変数(実引数)を変更してしまうようなバグを生む可能性があります。フィールド名はvalでなくf_valもしくは_valとしておいたほうが、スコープが明確になってぱっと見分かりやすいです。わざわざthisを使うよりもスマートかと。

class Test
{
    int val;

    void DoTest(ref int val)
    {
        val = 5; // パラメータ(ローカル変数)が優先される。
        this.val = 5; // フィールドを参照するため、スコープを明示する。
    }
}

なお、protectedフィールドやinternalフィールドは範囲が限定されるとはいえクラスの実装を外部に不用意にさらすことになり、publicフィールドとあまり大差ないので、基本的に使いません。

ちなみにDelphiではフィールド名をFで始めるルールが推奨されていましたが、これだとFで始まる単語を使うときに例えば FFontName とかいう読みづらい名前になってしまうので自分はあまり好きではありませんでした。

Javaの場合

JavaC#と違って構造体もプロパティも言語機能として存在しないため、ただのデータ集合としての型をクラスで定義する場合、フィールドをpublicもしくはデフォルトのpackage private (internal) にすることがよくあります。カプセル化の観点からは通例フィールドをpublicにするべきではありませんが、単なるデータ集合にすぎない型では、いちいちすべてのフィールドにgetter/setterアクセッサを用意するのはバカバカしいですね。
少なくとも、publicフィールドはプレフィックスもポストフィックスも付けず、小文字で始めるようにしています。privateフィールドは自由に付けてもよさそうですが、protectedやデフォルト (internal) アクセス指定子の場合はそうもいかないので、やはりフィールドには一律プレフィックスもポストフィックスも付けない方式がよいと思います。
もしメソッド引数の名前がフィールドと衝突する場合は、thisでスコープを明示します。これもフィールド参照には一律thisを付けておくようにすると分かりやすいです。

Javaが最初にリリースされたときにSunによって定められた公式命名規則では、変数名はアンダースコアで始めるべきではない、とされています。

UIコントロール変数名

GUIツールキットを使う際は、UIデザイナー(UIエディター)で指定したコントロール変数名の命名規則にも注意を払う必要があります。ではWindows Formsのコントロール変数名にもC#のようにf_/_プレフィックスを付けたり、XAML要素のName属性値にもf_/_プレフィックスを付けたりするかというと、これまた微妙です。これらのUIデザイナー(UIエディター)で指定した名前は、最終的にはコードジェネレーターによってC#のフィールド扱いとなるのですが、とりあえず自分は小文字で始めるようにしてます。この場合、コードビハインドのC#側でコントロール変数を参照するときは、thisを使って必ずスコープを明示します。

<Window
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
    Width="525"
    Height="350"
    WindowStartupLocation="CenterScreen"
    FontFamily="Segoe UI">
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button Name="buttonClose" Content="Close" Padding="4" HorizontalAlignment="Center"/>
    </Grid>
</Window>
namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.buttonClose.Click += (s, e) => { this.Close(); };
        }
    }
}

ちなみにUIコントロール型のメンバーのプレフィックスに関しては、VBVBAでは通例コントロールの識別子に特定のプレフィックス(コマンド ボタンだとcmd、テキスト ボックスだとtxtなど)を付けるルールが一般的だったのですが、自分はこれを踏襲してVC#のWindows Formsでもbtnやtxtなどのプレフィックスを使っていた時期がありました。.NET命名規則では省略形や頭字語は使ってはいけないことになっていますが、privateフィールドでしか使ってないので許せる範囲ではないでしょうか。
なお、MFCではDDXコントロール変数になるので、DDX/DDVに関係のない普通の変数と区別するため、m_ddxcEditUserNamem_ddxcComboUserCountryのような形で命名するようにしています。
ちなみにWPF/SilverlightXAMLでは、TextBlockとTextBoxという似たような字面の要素が基本ライブラリに存在するため、VB/VBAWindows Formsで使っていた省略形は一切使わずに、textblockCurrentTimetextboxUserNamecomboUserCountryのような形にしています。*4

ルール作り

これまで命名規則に関して自分なりの持論を述べてきましたが、変数の命名に頭を悩ませすぎるのはアホらしいし労力の無駄遣いなので、こういうのはとにかく何でもいいから一律決めたルールに従い続けるのが利口かと。チームでコーディングするときはルールを作り、ドキュメント化しましょう。ドキュメントにはGood caseとBad caseを載せておくとメンバーの意識を統一しやすいかもしれません。自分一人だけでコーディングするときもマイルールを決めておくとよいです。

というわけでプレフィックスはある程度機械的に決めてしまった方が生産性とメンテナンス性が上がる気がします。もちろん論理型や数値型に対して今更システムハンガリアンを適用するのはナンセンスです。今のC/C++において、bool, float, double, int, longなどの組み込み型や、WORD, DWORDなどのWindows APIシノニム型の変数に対して、それぞれb, f, d, i/n, l, w, dwなどのプレフィックス機械的に付けさせるようなシステムハンガリアンは害悪しかもたらさないので、絶対にやめましょう。またenumのeプレフィックスや、定数のkプレフィックス*5もときどき見かけますが、個人的な意見としては「センスがないな」と思います。

ちなみに自分が仕事でメンテしたことのある古いコードでは、

  • グローバル変数なのにやたら短くて判別しにくい名前になっている("g_"のようなスコープを表すプレフィックスがなく、ローカル変数と区別がつかない)
  • 変数なのにマクロシンボルやJavaenumメンバーのようにすべて大文字になっている
  • マクロ定数なのに全部大文字ではなくPascal方式のシンボル名になっている
  • ファイル名やシンボル名にローマ字日本語が使われている*6

といった、うんざりするような一貫性のない命名センスを持った開発者がいたことがよく分かるようになっていました。昔のMS-DOSやWin16、それとその時代の処理系ではファイル名やシンボル名の長さに制限があったことが背景にあるのも事実ですが、それにしてもお粗末。MFCのアプリケーションウィザードが生成するコードの影響もあると思います。

まとめ

命名センスというのは良いプログラムをたくさん読んで、良いライブラリ(近代的なライブラリ)を参考にすれば自然と身につくものだと思います。というわけでコードを読みましょう。そして良いものは積極的に取り入れてみましょう。また、言語やライブラリごとの作法というのもあるので、最低限そういったルールだけは押さえておくと後々苦労しなくてすみます。プログラミング言語自然言語を問わず、ひとつの言語だけに固執せず、複数の言語を学んで知見を取り入れることも重要で、命名センスのブラッシュアップにもつながります。

*1:2016-01-18追記:厳密に言うと、グローバルスコープではなく、なおかつアンダースコアの次が大文字でなければ規格違反にはならないようですが、そもそもアンダースコアで始まるシンボル名はユーザーコード内では一律使わないようにしておいたほうがルールとして分かりやすいし安全でしょう。boost::lambda::_1や、D3DXMATRIX::_11なども規格違反ではありませんが、あまり積極的に使ってよい命名規則ではありません。ちなみに二重のアンダースコアに関してはCとC++とで扱いが異なり、C++では二重のアンダースコアは先頭以外でも禁止されているようです。コードジェネレーターを書いてシンボル名を自動生成するときなどは注意しておいたほうがよいでしょう。C のキーワード - cppreference.com, C++ のキーワード - cppreference.com, 予約名 | Deep C++ | MSDN | Internet Archive, Identifiers (C++) | Microsoft Learn, DCL37-C. 予約済み識別子の宣言や定義をしない, [迷信] 構造体のタグ名は下線で始める | 株式会社きじねこ

*2:例えば "m_hoge" に対して "m_" とだけ入力してあとはインテリセンスに候補を表示してもらうのが楽なのですが、VC++ 2008以前の場合は後方一致や部分一致が効かないので、"hoge_" に対して "_" では候補表示されません。ちなみにVC++ 2010以降であれば前方一致だけでなく後方一致や部分一致も候補に挙げてくれます。

*3:MS製のツール(LINQ to DataSetなど)が自動生成するコードでもフィールド名がアンダースコアで始まっていることがあります。

*4:WPFの場合、双方向データ バインディングを使えば、コントロール変数にわざわざ名前を付ける機会というのは減るのですが、それでもなんらかの命名規則を設けておいたほうがよいでしょう。

*5:物理や化学では定数に kK を使うことが多いのですが、これはもともとドイツ語のkonstantが由来だそうです。

*6:たとえばHyokaとかKeijoとか。このような名前は日本人にしか分かりません。むしろ日本人であっても戸惑う。EvaluationやProfile/Geometry/Shapeなどの正式な英語のテクニカルタームを使うべきです。