内部監査では、キーワードを使用する代わりに、明示的な変数型宣言を使用することを提案していますvar
。var
彼らは、 「場合によっては予期しない結果につながる可能性がある」と主張しています。
var
明示的な型宣言と、コードがMSILにコンパイルされた後の使用との違いを認識していません。
監査人は尊敬されている専門家なので、私はそのような提案を単純に拒否することはできません。
内部監査では、キーワードを使用する代わりに、明示的な変数型宣言を使用することを提案していますvar
。var
彼らは、 「場合によっては予期しない結果につながる可能性がある」と主張しています。
var
明示的な型宣言と、コードがMSILにコンパイルされた後の使用との違いを認識していません。
監査人は尊敬されている専門家なので、私はそのような提案を単純に拒否することはできません。
これはどう...
double GetTheNumber()
{
// get the important number from somewhere
}
そして他の場所...
var theNumber = GetTheNumber();
DoSomethingImportant(theNumber / 5);
そして、将来のある時点で、誰かGetTheNumber
が整数のみを返すことに気付いたので、それをリファクタリングして、int
ではなく返すようにしdouble
ます。
バン!以前は浮動小数点演算であったものが、誰にも気付かれることなく整数演算になっているため、コンパイラエラーは発生せず、予期しない結果が発生し始めます。
そうは言っても、この種のことはユニットテストなどで捕らえられるべきですが、それでも潜在的な落とし穴です。
私はこのスキームに従う傾向があります:
var myObject = new MyObject(); // OK as the type is clear
var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear
リターンタイプがSomeMethod
変更された場合でも、このコードはコンパイルされます。最良の場合、コンパイルエラーがさらに発生しますが、最悪の場合(myObject
使用方法によっては)発生しない場合があります。その場合におそらく発生するのは、追跡が非常に難しい実行時エラーです。
varは動的タイプではなく、単にシンタックスシュガーです。これに対する唯一の例外は、匿名タイプの場合です。 MicrosoftDocsから
多くの場合、varの使用はオプションであり、構文上の便宜にすぎません。ただし、変数が匿名型で初期化される場合、後でオブジェクトのプロパティにアクセスする必要がある場合は、変数をvarとして宣言する必要があります。
タイプを暗黙のタイプとは異なるものとして明示的に定義しない限り、一度ILにコンパイルしても違いはありません(ただし、なぜそうするのかはわかりません)。コンパイラーでは、varで宣言された変数の型をいつでも変更することはできません。
Microsoftのドキュメントから(再び)
暗黙的に型付けされたローカル変数は、自分で型を宣言したかのように強く型付けされますが、コンパイラーが型を決定します
場合によっては、varは読みやすさを妨げる可能性があります。その他のMicrosoftドキュメントの状態:
varを使用すると、少なくとも他の開発者がコードを理解しにくくなる可能性があります。そのため、C#ドキュメントでは通常、必要な場合にのみvarを使用します。
場合によっては、予期しない結果が生じる可能性があります。私var
自身はファンですが、これはうまくいかない可能性があります。
var myDouble = 2;
var myHalf = 1 / myDouble;
明らかに、これは間違いであり、「予期しない結果」ではありません。しかし、それは落とし穴です...
非ジェネリックな世界では、ループvar
内などで暗黙的な変換が発生するたびに、型の代わりに使用すると異なる動作が発生する可能性があります。foreach
以下の例では、からobject
への暗黙の変換XmlNode
が行われます(非ジェネリックIEnumerator
インターフェイスはのみを返しますobject
)。ループ変数の明示的な宣言をキーワードに置き換えるだけでvar
、この暗黙的な変換は行われません。
using System;
using System.Xml;
class Program
{
static void Foo(object o)
{
Console.WriteLine("object overload");
}
static void Foo(XmlNode node)
{
Console.WriteLine("XmlNode overload");
}
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><child/></root>");
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
Foo(node);
}
foreach (var node in doc.DocumentElement.ChildNodes)
{
// oops! node is now of type object!
Foo(node);
}
}
}
var
その結果、このコードは、使用したタイプと明示的なタイプのどちらを使用したかに応じて、実際には異なる出力を生成します。オーバーロードを使用すると実行されます。それ以外の場合はvar
オーバーロードが実行されます。したがって、上記のプログラムの出力は次のようになります。Foo(object)
Foo(XmlNode)
XmlNodeの過負荷 オブジェクトの過負荷
この動作は、C#言語仕様に完全に準拠していることに注意してください。唯一の問題は、予想var
とは異なるタイプ(object
)を推論することと、この推論がコードを見ても明らかではないことです。
短くするためにILを追加しませんでした。ただし、必要に応じてildasmを調べて、コンパイラが2つのforeachループに対して実際に異なるIL命令を生成することを確認できます。
var
C#言語には、の使用よりもはるかに複雑な微妙な点があるため、「場合によっては予期しない結果につながる可能性がある」ため、使用してはならないというのは奇妙な主張ですvar
。
これらの1つは、匿名メソッドの実装の詳細であり、R#警告「変更されたクロージャへのアクセス」につながる可能性があり、コードを見ると期待するものとはまったく異なる動作になります。var
いくつかの文で説明できるものとは異なり、この動作には、完全に説明するための逆アセンブラの出力を含む3つの長いブログ投稿が必要です。
これは、匿名メソッド(つまり、デリゲート、ラムダ)や、LinqやParallelFXなどのそれらに依存するライブラリも使用してはならないことを意味します。これは、特定の奇妙な状況では、動作が期待どおりにならない可能性があるためです。
もちろん違います。
つまり、書いている言語を理解し、その制限とエッジケースを理解し、期待どおりに機能することをテストする必要があります。「場合によっては予期しない結果につながる可能性がある」という理由で言語機能を除外すると、使用する言語機能がほとんどなくなることになります。
彼らが本当にトスについて議論したいのであれば、あなたのバグの多くがの使用に直接起因する可能性がvar
あり、明示的な型宣言がそれらを防いだであろうことを実証するように彼らに依頼してください。すぐに返事が来るとは思えません。
彼らは、varの使用は「場合によっては予期しない結果につながる可能性がある」と主張しています。場合によっては予期しない結果につながる可能性があります。
「コードを読んで何をしているのかわからない」という予想外の場合は、そうです、予想外の結果につながる可能性があります。コンパイラは、変数の周囲に記述されたコードに基づいて、変数を作成するタイプを認識している必要があります。
varキーワードは、コンパイル時の機能です。コンパイラーは、宣言に適切な型を入れます。これが、次のようなことができない理由です。
var my_variable = null
or
var my_variable;
varキーワードは、コード自体で定義する必要のある情報が少ないため、優れています。コンパイラーは、それがあなたのために何をすべきかを理解します。これは、インターフェイスを使用するときに常にプログラミングするのとほぼ同じです(インターフェイスのメソッドとプロパティは、varで定義された変数の宣言スペース内で使用するものによって定義されます)。変数の型を変更する必要がある場合(当然のことながら)、変数宣言の変更について心配する必要はありません。コンパイラーがこれを処理します。これは些細なことのように聞こえるかもしれませんが、関数の戻り値を変更する必要があり、その関数がプログラム全体で使用されている場合はどうなりますか。varを使用しなかった場合は、変数が呼び出されるすべての場所を見つけて置き換える必要があります。varキーワードを使用すると、それについて心配する必要はありません。
ガイドラインを考え出すとき、監査人がしなければならないように、人々に単に賢明で正しいことをするように言うのではなく、愚か者の安全の側で誤りを犯したほうがおそらく良いでしょう。手元の状況の評価に基づくもの。
「コードのどこにも使用しないでください」と言うだけvar
で、コーディングガイドラインの多くのあいまいさを取り除くことができます。これにより、いつこれを行うか、いつ行うかという問題を解決することなく、コードのルックアンドフィールがより標準化されるはずです。
私は個人的に大好きvar
です。すべてのローカル変数に使用します。いつも。結果のタイプが明確でない場合、これはの問題ではなくvar
、変数の初期化に使用されるメソッド(の名前付け)の問題です。
var
変数がどのタイプであるかが明確な場合、またはタイプをまったく知る必要がない場合にのみ使用します(たとえば、GetPerson()はPerson
、Person_Class
などを返す必要があります)。
var
プリミティブ型、列挙型、文字列には使用しません。また、値型は代入によってコピーされるため、変数の型を明示的に宣言する必要があるため、値型には使用しません。
監査人のコメントについては、私たちが毎日行っているようにコード行を追加すると、 「場合によっては予期しない結果につながる」と言えます。この引数の有効性は、私たちが作成したバグによってすでに証明されているため、それを防ぐためにコードベースを永久にフリーズすることをお勧めします。
varキーワードの使用に関しては、単純な原則に従います。事前にタイプがわかっている場合は、varを使用しないでください。ほとんどの場合、匿名型を返したいので、linqでvarを使用します。
明らかに宣言がある場合に使用するのに最適なvar
ArrayList<Entity> en = new ArrayList<Enity>()
読みやすさを複雑にします
var en = new ArrayList<Entity>()
怠惰で明確なコード、私はそれが好きです
var
を使用した変数宣言と明示的に指定された変数宣言のIL出力にまったく違いはありません(これはリフレクターを使用して証明できます)。すべてを明示的に指定したいので、私は通常var
、長いネストされたジェネリック型、ループ、および匿名型にのみ使用します。foreach
他の人は異なる好みを持っているかもしれません。
varは、明示的な型宣言を使用するための省略表記です。
varは特定の状況でのみ使用できます。varを使用する場合は、宣言時に変数を初期化する必要があります。後で別のタイプの変数を変数に割り当てることはできません。
多くの人が「var」キーワードをVB6の「Variant」データ型と混同する傾向があるように思われます。
明示的な変数宣言を使用することに関して私が見る「唯一の」利点は、適切に選択された型名を使用して、コードの意図をはるかに明確に示すことです(これは他の何よりも重要です)。varキーワードの利点は、実際にはPieterが言ったことです。
タイプがどうなるかがわかっている場合、varの使用は怠惰なコードです。読みやすく、すっきりしています。たくさんのコードを見るときは、より簡単でクリーンな方が常に優れています
また、最後にDを付けずにダブルスを宣言すると、問題が発生すると思います。リリースバージョンをコンパイルするとき、コンパイラは精度を考慮しないため、スペースを節約するためにdoubleを取り除き、floatにする可能性があります。
varは、指定できる静的型と同じものにコンパイルされます。コード内でそのタイプを明示する必要がなくなるだけです。これは動的タイプではなく、実行時に変更されない/変更されません。foreachループで使用すると非常に便利です。
foreach(var item in items)
{
item.name = ______;
}
列挙型を操作する場合、特定のタイプが不明であり、検索に時間がかかることがあります。静的タイプの代わりにvarを使用すると、同じ結果が得られます。また、varを使用すると、リファクタリングが容易になることもわかりました。別のタイプの列挙を使用する場合、foreachを更新する必要はありません。
varを使用すると、論理プログラミングエラーが隠される可能性があります。そうしないと、コンパイラまたはIDEから警告が表示されます。この例を参照してください。
float distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);
ここでは、計算のすべてのタイプがであり、結果を変数int
で取得するため、分数が失われる可能性について警告が表示されます。float
varの使用:
var distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);
distX
のタイプはとしてコンパイルされているため、ここでは警告は表示されませんint
。float値を使用する場合、これは論理エラーでありdivide by zero
、この最初の計算の結果が1未満の場合に後の計算で例外がトリガーされない限り、実行時に特定するのは困難です。