公開されたフィールドは存在が悪であるということ

ついでに類似ネタ。上記の auto property が活躍できるような場面では、私は仕事以外では好きで public なフィールドを多用しています。しかし、実は .NET の世界において、フィールドは private または internal までであるべきで、protected 以上の可視性をもつものは、存在そのものが問題を抱えており、それが外部ライブラリなどから表面化したときには「なんでフィールドなんだ!」と叫びたくなること間違いなし。*1

メタデータの差異

フィールドとプロパティの最も大きな違いは、メタデータの差異です。メタデータは Native から Managed へ移行するにあたって、大きな変更点として真っ先にあげられるものの1つであるぐらいに .NET では重要な位置に存在します。
フィールド型のメタデータとプロパティ型のメタデータにはいくつかの差異がありますが、データバインディングをはじめとする多くのテクノロジの実装ががプロパティ型のメタデータのみを受け入れます。同様に CAS などの制限もプロパティ型のメタデータにしか適用されません。

読み取り専用の差異

readonly フラグの設定されたフィールドと、 .NET 2.0 以降の可視性による set の制御付きプロパティによるプロパティの差異は大きなところです。public readonly のフィールドをプロパティに変更する大きな理由の1つに、コンストラクション以降に値の変更を余儀なくされた場合というのが最も多いと経験しています。

実行時の動作の違い

.NET のフィールドはよくできていて、C/C++ の「初期化」と「代入」の差異をきちんともっています。しかし、C# をはじめとする多くの .NET 対応プログラム言語では、この2種類の違いを表現する構文/手段をもたず、初期値を持ったフィールドの定義が初期化済みメモリの確保であるのか、メモリ確保後に初期化操作での代入であるのかを一見して判断することができない、という致命的な問題をかかえています。
このことは、プロパティへ変更することで完全に解決するものではありませんが、バックストアフィールドに対する初期化を行うような実装を記載しないかぎり避けることができます。*2
また、JIT コンパイラはプロパティの最適化がかなりチューニングされていて実行時のパフォーマンスはフィールドと比べて大きな見劣りはありません。

修正すると互換性が失われる

途中でも記載しましたが、公開されたフィールドをプロパティに変換するというのは、フィールドを公開していた人にとっては、必要に迫られて実施するのはたやすいレベルです。

public int PublicField;
protected readonly bool ProtectedField;

のような記述を

private int publishedValue;
public int PublicField
{
  get { return this.publishedValue; }
  set { this.publishedValue = value; }
}

private bool familyFlag;
protected bool ProtectedField
{
  get { return this.familyFlag; }
  private set
  {
    this.familyFlag = value;
  }
}

のように書き換えることで、ソースコードの互換性を保ったままフィールドからプロパティへの変換を実施することができます。
しかし、ソースコードの互換性は保たれるものの、バイナリ互換性は保たれていないことには注意が必要です。
public なフィールドを使用していたコンシューマクラスや、protected なフィールドを使用していたセカンドパーティクラスがアセンブリ境界をまたいで存在していたら、.NET のアセンブリバインディングを正しく動作させるために、バージョン番号の第二レベルを1以上大きな値にすることが *必須* となります。現在のアセンブリのバージョンが 3.0.x.y であれば、3.1.0.0 以上にしなければならないということです。
新しいバージョンのアセンブリを使うためには、関連プロダクトはすべて再コンパイルされなければならず、そうでないプロダクトと共存するためにはサイドバイサイドによるアセンブリ管理を必要とし、GAC に登録しないアセンブリとして bin ディレクトリに保存されていたりしたら、サイドバイサイド読み込みが実施されるように、手動ファイル管理されるような再配置まで必要になります。*3
こうなると、問題は突然に巨大化します。インストーラやマージモジュールは作り直しが必要になり、社内モジュールにしても外部へ公開しているライブラリや製品にしても、利用者にはバージョンアップの通知を行わないといけなくなるし、利用者が製品として売り出していたら、その製品も再コンパイルしてバージョンアップが発生する。サイドバイサイド配置の再設計が必要なのは、自分達ではなくライブラリを利用した他人である可能性も高くなるなど、あちこちに飛び火しまくりです。

どの問題も

アプリケーションの規模や、開発段階において、なかなか発生しない問題ばかりです。ライブラリを売り出してから半年してから発覚した!とか、余裕でありえそうなレベルです。そういう問題であるからこそ、いざ修正しようとしたら副産物的な問題が根をおろしていて一大事になってしまうのです。

1つのフィールドをプロパティへ修正するぐらいなら一瞬でおわっちゃいます。色々な問題が根をはらないうちに、リリース前に、コードレビューのうちに、開発初期段階に、社内コーディング規約のうちに、できるだけ早い段階で公開レベルの高いフィールドは駆除しておきましょう。

*1:そんなことがわかってるのに多用してるのはごめんなさいとしか言いようがない

*2:auto property は、バックストアフィールドを参照できないので、この問題を完全に解決することができます

*3:単一フォルダに複数のアプリケーションを導入してアセンブリを共有する場合、最初からサイドバイサイドを考慮した配置をするべき、というのはおいといて