何
これは、多くの暗黙的な型変換を伴う言語で使用されるコーディング スタイルです ( OSVの語順から、スター ウォーズのキャラクターに由来するフレンドリーな名前のヨーダ)。タイプミスに起因するある種のエラーを防ぐために、プロジェクトのガイドラインによって強制されたり、要求されたりすることさえあります。言語が異なれば、このスタイルのバリエーションと拡張も異なりますが、この回答は C と C# に限定します。
なぜいいの
たとえば、C では次のように記述できます。
int a = CalculateValue();
if (a = CalculateAnotherValue()) {
/* Do something */
}
a
このコードは、返された値に代入しCalculateValue()
、結果で値を上書きし、CalculateAnotherValue()
ゼロでない場合はコードを実行します。おそらくそれは意図したものではなく、式の割り当てがif
間違っています。あなたの例から、NULL
あなたは持っているかもしれません:
if (pBuffer = NULL)
繰り返しますが、おそらくそれはあなたが望むものではありません (そして、これは非常によくある間違いです)。ポインターに NULL を割り当てると、条件は常に false になります。あなたが書く場合:
if (NULL = pBuffer)
(値をリテラルに代入できないため) コンパイルされず、コンパイル時エラーが発生します。
この種のコンパイル時チェックは、このコーディング スタイルを使用する唯一の理由ではありません。次の C# (非常に一般的な) コードを見てください。
if (text != null && text.Equals("something", StringComparison.InvariantCulture)
DoSomething();
次のように契約できます。
if ("something".Equals(text, StringComparison.InvariantCulture))
DoSomething();
なんでだめなの
C#では、通常は問題になりません。これは C/C++ から継承されたプラクティスです。C# では式が自動的に変換されないためbool
、次のコードはコンパイルされません。
if (Request.QueryString["PartnerID"] = null)
次に、この慣行はC#では役に立ちません(コメントで@IlianPinzonが指摘した例外を除いて)、パフォーマンスについては何もありません。このような間違いを避けるためだけに使用されました。
なぜそうかのセクションの最後の例については、問題は読みやすさ"something".Equals(text)
です。
パフォーマンス
これらの関数から始めます:
static bool TestRight(object value)
{ return value == null; }
static bool TestLeft(object value)
{ return null == value; }
以下の IL を生成します。
.maxstack 2
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldnull
L_0002: ldarg.0
L_0003: ceq
L_0005: stloc.0
L_0006: br.s L_0008
L_0008: ldloc.0
L_0009: ret
唯一の違いは、L_0001 と L_0002 の行です。これらは入れ替わっているだけですが、オペランドの順序によって の動作が変わることはありませんceq
。メソッドをオーバーライドしてもEquals()
、JIT コンパイラは両方の式に対して同じアセンブリ コードを生成します (比較は常にEquals()
, null
is typelessによって行われるため)。
比較にユーザー定義の等値比較子が含まれる場合、事態はさらに複雑になる可能性があります。この場合、ルールはなく、効果的なop_Equals
実装に依存します。たとえば、この==
実装:
public static bool operator==(MyType lhs, MyType rhs)
{
if (Object.ReferenceEquals(lhs, rhs))
return true;
if (Object.ReferenceEquals(lhs, null))
return false;
return lhs.Equals(rhs);
}
この場合、最初のオペランドが実行である場合null
、実行はわずかに高速になりますが (MyType.Equals()
呼び出されないため)、このパフォーマンスの向上は非常に小さく、1 つの比較、いくつかのジャンプ、および仮想関数呼び出しを節約できます。さらに、逆の場合に関数をより高速に書き直すことができます (これが本当に重要な場合)。
ここでは、パラメーター以外の値をMyType.Equals(object)
単純に返す小さなテストを示します。テストはループ回数になります。true
null
Int32.MaxValue
操作合計時間 (ミリ秒)
lhs == null 10521
null == lhs 2346
の不要な呼び出しを回避する「最適化された」バージョンEquals()
は 5 倍速いようですが、ループ回数が非常に多く、実際の実装Equals()
は空であることに注意してください。真の実装では、関数呼び出しの相対的なオーバーヘッドが削減されます (おそらく、このマイクロ最適化以外にやるべきことがあります)。システム クラスの場合、この詳細に依存することはできません。たとえば、String.Equals()
常にオペレーターによって呼び出されます (順序は関係ありません)。ただし、1 つのコード パスの方が高速であると想定することはできず、この事実はフレームワークの将来のバージョンでも変更されません。 (または異なる CPU アーキテクチャの場合)。