読者です 読者をやめる 読者になる 読者になる

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

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

メンバー変数命名規則

プログラミングTips C++ C#

(これは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は名著だが、サンプルコードがポストアンダースコアなのが好きではない)。

C++ Coding Standards―101のルール、ガイドライン、ベストプラクティス (C++ in‐depth series)

C++ Coding Standards―101のルール、ガイドライン、ベストプラクティス (C++ in‐depth series)

  • 作者: ハーブサッター,アンドレイアレキサンドレスク,浜田光之,Herb Sutter,Andrei Alexandrescu,浜田真理
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2005/10
  • メディア: 単行本
  • 購入: 20人 クリック: 383回
  • この商品を含むブログ (98件) を見る


ただ、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; // フィールドを参照するため、スコープを明示する。
    }
}

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

UIコントロール変数名

GUIツールキットを使う際は、UIデザイナー(UIエディター)で指定したコントロール変数名の命名規則にも注意を払う必要があります。ではWindows Formsのコントロール変数名にもC#のようにf_/_プレフィックスを付けたり、XAML要素のName属性値にもf_/_プレフィックスを付けたりするかというと、これまた微妙です。これらのUIデザイナー(UIエディター)で指定した名前は、最終的にはコードジェネレーターによってC#のprivateフィールド扱いとなるのですが、とりあえず自分は小文字で始めるようにしてます。この場合、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_ddxcEditUserNameとかm_ddxcComboUserPrefectureとかいう感じで命名するようにしています。
ちなみにWPF/SilverlightXAMLでは、TextBlockとTextBoxという似たような字面の要素が基本ライブラリに存在するため、VB/VBAWindows Formsで使っていた省略形は一切使わずに、textblockCurrentTimeとかtextboxUserNameとかcomboUserPrefectureとかにしています。*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プレフィックスもときどき見かけますが、個人的な意見としては「センスがないな」と思います。

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

  • グローバル変数なのにやたら短くて判別しにくい名前になっている("g_"のようなスコープを表すプレフィックスがなく、ローカル変数と区別がつかない)
  • 変数なのにマクロやenumメンバーみたいにすべて大文字になっている
  • マクロ定数なのに全部大文字じゃなくてPascal方式のシンボル名になっている
  • ファイル名やシンボル名にローマ字日本語が使われている(たとえばHyokaとかKeijoとか。EvaluationとかProfile/Geometry/Shapeとかの英語のテクニカルタームを使うべきでしょう……)

といった、うんざりするような命名センスを持った開発者がいたことがよく分かるようになっていました。昔のMS-DOSやWin16、それとその時代の処理系ではファイル名やシンボル名の長さに制限があったことが背景にあるのも事実ですが、それにしてもお粗末。

まとめ

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

*1:2016-01-18追記:厳密に言うと、グローバルスコープではなくなおかつアンダースコアの次が小文字であれば規格違反にはならないようですが、そもそもアンダースコアで始まるシンボル名はユーザーコード内では一律使わないようにしておいたほうがルールとして分かりやすいし安全でしょう。ちなみに二重のアンダースコアに関してはCとC++とで扱いが異なり、C++では二重のアンダースコアは先頭以外でも禁止されているようです。コードジェネレーターを書いてシンボル名を自動生成するときなどは注意しておいたほうがよいでしょう。https://msdn.microsoft.com/ja-jp/library/cc440188.aspx

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

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

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