C# Microsoftを対象とするいくつかの言語の1つです 共通言語ランタイム (CLR)。 CLRを対象とする言語は、言語間の統合と例外処理、強化されたセキュリティ、コンポーネントの相互作用の簡素化されたモデル、デバッグおよびプロファイリングサービスなどの機能の恩恵を受けます。今日のCLR言語の中で、C#は複雑でプロフェッショナルな言語に最も広く使用されています 開発プロジェクト これは、Windowsデスクトップ、モバイル、またはサーバー環境を対象としています。
C#は、オブジェクト指向の強い型の言語です。コンパイル時と実行時の両方でC#の厳密な型チェックを行うと、一般的なC#プログラミングエラーの大部分ができるだけ早く報告され、その場所が非常に正確に特定されます。これにより、時間を大幅に節約できます。 Cシャーププログラミング 、型安全性の実施により寛大な言語で問題のある操作が行われたずっと後に発生する可能性のある不可解なエラーの原因を追跡することと比較して。ただし、多くのC#コーダーは、無意識のうちに(または不注意に)この検出の利点を捨ててしまい、このC#チュートリアルで説明されている問題のいくつかにつながります。
このチュートリアルでは、C#プログラマーが犯した最も一般的な10のC#プログラミングの間違い、または回避すべき問題について説明し、ヘルプを提供します。
この記事で説明する間違いのほとんどはC#固有のものですが、CLRを対象とする、またはCLRを利用する他の言語にも関連するものもあります。 フレームワーククラスライブラリ (FCL)。
C ++や他の多くの言語のプログラマーは、変数に割り当てる値が単なる値であるか、既存のオブジェクトへの参照であるかを制御することに慣れています。ただし、C Sharpプログラミングでは、その決定は、オブジェクトをインスタンス化して変数に割り当てるプログラマーではなく、オブジェクトを作成したプログラマーによって行われます。これは、C#プログラミングを学ぼうとする人にとって一般的な「落とし穴」です。
使用しているオブジェクトが値型か参照型かわからない場合は、いくつかの驚きに遭遇する可能性があります。例えば:
Point point1 = new Point(20, 30); Point point2 = point1; point2.X = 50; Console.WriteLine(point1.X); // 20 (does this surprise you?) Console.WriteLine(point2.X); // 50 Pen pen1 = new Pen(Color.Black); Pen pen2 = pen1; pen2.Color = Color.Blue; Console.WriteLine(pen1.Color); // Blue (or does this surprise you?) Console.WriteLine(pen2.Color); // Blue
ご覧のとおり、Point
の両方およびPen
オブジェクトはまったく同じ方法で作成されましたが、point1
の値は新しいX
が変更されないままでした座標値はpoint2
に割り当てられましたが、pen1
の値は だった 新しい色がpen2
に割り当てられたときに変更されました。したがって、私たちはできます 演繹 そのpoint1
およびpoint2
それぞれにPoint
の独自のコピーが含まれていますオブジェクト、pen1
およびpen2
同じPen
への参照が含まれていますオブジェクト。 しかし、この実験を行わずに、どうすればそれを知ることができますか?
答えは、オブジェクトタイプの定義を確認することです(これは、Visual Studioで、オブジェクトタイプの名前の上にカーソルを置き、F12キーを押すことで簡単に実行できます)。
public struct Point { ... } // defines a “value” type public class Pen { ... } // defines a “reference” type
上に示したように、C#プログラミングでは、struct
キーワードは値型を定義するために使用され、class
キーワードは、参照型を定義するために使用されます。 C ++とC#のキーワードの多くの類似点によって誤った安心感に陥った、C ++のバックグラウンドを持つ人々にとって、この動作は、C#チュートリアルからの助けを求めるかもしれない驚きとして来る可能性があります。
オブジェクトをメソッドパラメータとして渡し、そのメソッドにオブジェクトの状態を変更させる機能など、値と参照型の間で異なる動作に依存する場合は、 C#プログラミングの問題を回避するための正しいタイプのオブジェクト。
C#では、値型をnullにすることはできません。定義上、値型には値があり、値型の初期化されていない変数でも値が必要です。これは、そのタイプのデフォルト値と呼ばれます。これにより、変数が初期化されていないかどうかを確認すると、通常は予期しない結果が発生します。
class Program { static Point point1; static Pen pen1; static void Main(string[] args) { Console.WriteLine(pen1 == null); // True Console.WriteLine(point1 == null); // False (huh?) } }
なぜpoint1
ではないのですかヌル?答えはPoint
ですは値型であり、Point
のデフォルト値です。 nullではなく(0,0)です。これを認識できないことは、C#で行う非常に簡単な(そして一般的な)間違いです。
多くの(すべてではありませんが)値型にはIsEmpty
がありますデフォルト値と等しいかどうかを確認できるプロパティ:
Console.WriteLine(point1.IsEmpty); // True
変数が初期化されているかどうかを確認するときは、そのタイプの初期化されていない変数がデフォルトでどの値を持つかを確認し、nullであることに依存しないでください。
C#で文字列を比較する方法はたくさんあります。
多くのプログラマーは==
を使用していますが文字列比較の演算子です。実際には、 少なくとも 主に、どのタイプの比較が必要かをコードで明示的に指定していないため、採用するのに望ましい方法。
むしろ、C#プログラミングで文字列の同等性をテストするための好ましい方法は、Equals
を使用することです。方法:
public bool Equals(string value); public bool Equals(string value, StringComparison comparisonType);
最初のメソッドシグネチャ(つまり、comparisonType
パラメータなし)は、実際には==
を使用する場合と同じです。演算子ですが、文字列に明示的に適用されるという利点があります。文字列の序数比較を実行します。これは基本的にバイトごとの比較です。多くの場合、これはまさに必要なタイプの比較です。特に、ファイル名、環境変数、属性など、値がプログラムで設定されている文字列を比較する場合はそうです。これらの場合、通常の比較が実際に正しいタイプである限り、その状況での比較の、Equals
を使用することの唯一の欠点comparisonType
のないメソッドコードを読んでいる誰かが、あなたが行っている比較のタイプを知らないかもしれないということです。
Equals
を使用するcomparisonType
を含むメソッドシグネチャただし、文字列を比較するたびに、コードが明確になるだけでなく、どのタイプの比較を行う必要があるかを明確に考えることができます。これはやりがいのあることです。なぜなら、英語では順序と文化に敏感な比較の違いがあまりない場合でも、他の言語では十分な違いがあり、他の言語の可能性を無視することで、将来のエラー。例えば:
string s = 'strasse'; // outputs False: Console.WriteLine(s == 'straße'); Console.WriteLine(s.Equals('straße')); Console.WriteLine(s.Equals('straße', StringComparison.Ordinal)); Console.WriteLine(s.Equals('Straße', StringComparison.CurrentCulture)); Console.WriteLine(s.Equals('straße', StringComparison.OrdinalIgnoreCase)); // outputs True: Console.WriteLine(s.Equals('straße', StringComparison.CurrentCulture)); Console.WriteLine(s.Equals('Straße', StringComparison.CurrentCultureIgnoreCase));
最も安全な方法は、常にcomparisonType
を提供することです。 Equals
へのパラメータ方法。基本的なガイドラインは次のとおりです。
CurrentCulture
またはCurrentCultureIgnoreCase
)を使用します。Ordinal
またはOrdinalIgnoreCase
)を使用します。InvariantCulture
およびInvariantCultureIgnoreCase
通常の比較がより効率的であるため、非常に限られた状況を除いて、一般的には使用されません。カルチャを意識した比較が必要な場合は、通常、現在のカルチャまたは別の特定のカルチャに対して実行する必要があります。Equals
に加えてメソッド、文字列もCompare
を提供しますメソッド。これは、同等性のテストだけでなく、文字列の相対的な順序に関する情報を提供します。この方法は、<
、<=
、>
よりも適していますおよび>=
上記と同じ理由で、C#の問題を回避するための演算子。
C#3.0では、 統合言語クエリ (LINQ)言語への変更は、コレクションのクエリと操作の方法を永久に変更しました。それ以来、コレクションを操作するために反復ステートメントを使用している場合は、おそらく必要なときにLINQを使用していませんでした。
ギリシャの債務危機の理由
一部のC#プログラマーは、LINQの存在すら知りませんが、幸いなことに、その数はますます少なくなっています。ただし、LINQキーワードとSQLステートメントは類似しているため、データベースをクエリするコードでのみ使用されると多くの人が考えています。
データベースクエリはLINQステートメントの非常に一般的な使用法ですが、実際には、列挙可能なコレクション(つまり、IEnumerableインターフェイスを実装するオブジェクト)に対して機能します。したがって、たとえば、アカウントの配列がある場合、C#リストforeachを作成する代わりに、次のようにします。
decimal total = 0; foreach (Account account in myAccounts) { if (account.Status == 'active') { total += account.Balance; } }
あなたはただ書くことができます:
decimal total = (from account in myAccounts where account.Status == 'active' select account.Balance).Sum();
これは、この一般的なC#プログラミングの問題を回避する方法の非常に単純な例ですが、1つのLINQステートメントで、コード内の反復ループ(またはネストされたループ)内の数十のステートメントを簡単に置き換えることができる場合があります。また、一般的なコードが少ないということは、バグが発生する機会が少ないことを意味します。ただし、パフォーマンスの面でトレードオフが発生する可能性があることに注意してください。パフォーマンスが重要なシナリオでは、特に反復コードがコレクションについてLINQでは不可能であると想定できる場合は、必ず2つの方法のパフォーマンスを比較してください。
LINQは、コレクションがメモリ内オブジェクト、データベーステーブル、またはXMLドキュメントであるかどうかに関係なく、コレクションを操作するタスクを抽象化するのに最適です。完璧な世界では、基礎となるオブジェクトが何であるかを知る必要はありません。しかし、ここでの誤りは、私たちが完璧な世界に住んでいると仮定していることです。実際、同じLINQステートメントをまったく同じデータで実行すると、そのデータが異なる形式である場合、異なる結果が返される可能性があります。
たとえば、次のステートメントについて考えてみます。
decimal total = (from account in myAccounts where account.Status == 'active' select account.Balance).Sum();
オブジェクトの1つがaccount.Status
の場合はどうなりますか「アクティブ」に等しい(大文字のAに注意)?ええと、myAccounts
DbSet
でしたオブジェクト(デフォルトの大文字と小文字を区別しない構成でセットアップされた)、where
式は引き続きその要素と一致します。ただし、myAccounts
の場合インメモリ配列にあった場合、一致しないため、合計で異なる結果が得られます。
しかし、ちょっと待ってください。以前に文字列の比較について話したとき、==
がオペレーターは文字列の序数比較を実行しました。では、なぜこの場合は==
なのか大文字と小文字を区別しない比較を実行するオペレーター?
答えは、LINQステートメントの基になるオブジェクトがSQLテーブルデータへの参照である場合(この例のEntity Framework DbSetオブジェクトの場合のように)、ステートメントはT-SQLステートメントに変換されるということです。次に、演算子はC#プログラミングルールではなくT-SQLプログラミングルールに従うため、上記の場合の比較では大文字と小文字が区別されません。
一般に、LINQはオブジェクトのコレクションをクエリするための便利で一貫した方法ですが、実際には、コードの動作が確実に行われるように、ステートメントが内部でC#以外のものに変換されるかどうかを知る必要があります。実行時に期待どおりになります。
前述のように、LINQステートメントはIEnumerableを実装するすべてのオブジェクトで機能します。たとえば、次の単純な関数は、アカウントのコレクションの残高を合計します。
public decimal SumAccounts(IEnumerable myAccounts) { return myAccounts.Sum(a => a.Balance); }
上記のコードでは、myAccountsパラメーターのタイプはIEnumerable
として宣言されています。 myAccounts
以降Sum
を参照しますメソッド(C#はおなじみの「ドット表記」を使用してクラスまたはインターフェイスのメソッドを参照します)、Sum()
というメソッドが表示されると予想されます。 IEnumerable
の定義についてインターフェース。ただし、IEnumerable
の定義は、Sum
を参照していません。メソッドと単純に次のようになります:
public interface IEnumerable : IEnumerable { IEnumerator GetEnumerator(); }
では、Sum()
はどこにありますかメソッドが定義されていますか? C#は強く型付けされているため、Sum
への参照がメソッドが無効だった場合、C#コンパイラは確かにエラーとしてフラグを立てます。したがって、それが存在しなければならないことはわかっていますが、どこにあるのでしょうか。さらに、LINQがこれらのコレクションをクエリまたは集約するために提供する他のすべてのメソッドの定義はどこにありますか?
答えはSum()
ですIEnumerable
で定義されたメソッドではありませんインターフェース。むしろ、System.Linq.Enumerable
で定義されているのは静的メソッド(「拡張メソッド」と呼ばれます)です。クラス:
namespace System.Linq { public static class Enumerable { ... // the reference here to “this IEnumerable source” is // the magic sauce that provides access to the extension method Sum public static decimal Sum(this IEnumerable source, Func selector); ... } }
では、拡張メソッドが他の静的メソッドと異なる点と、他のクラスで拡張メソッドにアクセスできるようにするものは何ですか?
拡張メソッドの際立った特徴はthis
です。最初のパラメーターの修飾子。これは、コンパイラに対して拡張メソッドとしてそれを識別する「魔法」です。変更するパラメーターのタイプ(この場合はIEnumerable
)は、このメソッドを実装するように見えるクラスまたはインターフェースを示します。
(補足として、IEnumerable
インターフェースの名前と拡張メソッドが定義されているEnumerable
クラスの名前の類似性については、魔法のようなものは何もありません。この類似性は、任意のスタイルの選択にすぎません。 。)
この理解により、sumAccounts
もわかります。上で紹介した関数は、代わりに次のように実装できます。
public decimal SumAccounts(IEnumerable myAccounts) { return Enumerable.Sum(myAccounts, a => a.Balance); }
代わりに、この方法で実装できたという事実は、なぜ拡張メソッドがあるのかという疑問を提起します。 拡張メソッド は本質的にC#プログラミング言語の便利な機能であり、新しい派生型を作成したり、再コンパイルしたり、元の型を変更したりすることなく、既存の型にメソッドを「追加」できます。
拡張メソッドは、using [namespace];
を含めることでスコープに組み込まれます。ファイルの先頭にあるステートメント。探している拡張メソッドがどのC#名前空間に含まれているかを知る必要がありますが、探しているものがわかれば、それを簡単に判断できます。
C#コンパイラは、オブジェクトのインスタンスでメソッド呼び出しを検出し、参照されるオブジェクトクラスで定義されているメソッドが見つからない場合、スコープ内にあるすべての拡張メソッドを調べて、必要なメソッドに一致するメソッドを見つけようとします。署名とクラス。見つかった場合は、インスタンス参照をその拡張メソッドの最初の引数として渡し、残りの引数がある場合は、後続の引数として拡張メソッドに渡します。 (C#コンパイラがスコープ内に対応する拡張メソッドを見つけられない場合、エラーがスローされます。)
拡張メソッドは、C#コンパイラの「シンタックスシュガー」の一例です。これにより、(通常は)より明確で保守しやすいコードを記述できます。より明確に、つまり、それらの使用法を知っている場合。そうしないと、特に最初は少し混乱する可能性があります。
拡張メソッドを使用することには確かに利点がありますが、それらを認識していない、または適切に理解していない開発者にとって、問題やC#プログラミングヘルプの要求を引き起こす可能性があります。これは、オンラインでコードサンプルを見るとき、または他の事前に作成されたコードを見るときに特に当てはまります。このようなコードがコンパイラエラーを生成する場合(呼び出されるクラスで明確に定義されていないメソッドを呼び出すため)、コードが別のバージョンのライブラリまたは別のライブラリに完全に適用されると考える傾向があります。存在しない新しいバージョン、またはファントムの「不足しているライブラリ」の検索に多くの時間が費やされる可能性があります。
拡張メソッドに精通している開発者でさえ、オブジェクトに同じ名前のメソッドがあると、時々捕まることがありますが、そのメソッドのシグネチャは拡張メソッドのシグネチャとは微妙に異なります。そこにないタイプミスやエラーを探すのに多くの時間が無駄になる可能性があります。
C#ライブラリでの拡張メソッドの使用はますます普及しています。 LINQに加えて、 Unityアプリケーションブロック そしてその WebAPIフレームワーク は、拡張メソッドも使用するMicrosoftによって頻繁に使用される2つの最新のライブラリの例であり、他にも多数あります。フレームワークが最新であるほど、拡張メソッドが組み込まれる可能性が高くなります。
もちろん、独自の拡張メソッドを作成することもできます。ただし、拡張メソッドは通常のインスタンスメソッドと同じように呼び出されるように見えますが、これは実際には単なる幻想であることに注意してください。特に、拡張メソッドは、拡張するクラスのプライベートメンバーまたは保護されたメンバーを参照できないため、従来のクラス継承の完全な代替として機能することはできません。
C#は多種多様なコレクションオブジェクトを提供しますが、以下は部分的なリストにすぎません。 Array
、ArrayList
、BitArray
、BitVector32
、Dictionary
、HashTable
、HybridDictionary
、List
、NameValueCollection
、OrderedDictionary
、Queue, Queue
、SortedList
、Stack, Stack
、StringCollection
、StringDictionary
。
選択肢が多すぎると選択肢が不十分になる場合もありますが、コレクションオブジェクトの場合はそうではありません。利用可能なオプションの数は間違いなくあなたの利点に働くことができます。事前に少し時間を取って調査し、目的に最適なコレクションタイプを選択してください。これにより、パフォーマンスが向上し、エラーの余地が少なくなる可能性があります。
持っている要素のタイプ(文字列やビットなど)を特に対象としたコレクションタイプがある場合は、最初にそれを使用する傾向があります。一般に、特定のタイプの要素を対象とする場合、実装はより効率的です。
C#の型安全性を利用するには、通常、非ジェネリックインターフェイスよりもジェネリックインターフェイスを優先する必要があります。ジェネリックインターフェイスの要素は、オブジェクトを宣言するときに指定するタイプですが、非ジェネリックインターフェイスの要素はオブジェクトタイプです。非ジェネリックインターフェイスを使用する場合、C#コンパイラはコードのタイプチェックを実行できません。また、プリミティブ値型のコレクションを処理する場合、非ジェネリックコレクションを使用すると繰り返し発生します ボクシング/開封 これらのタイプのうち、適切なタイプのジェネリックコレクションと比較した場合、パフォーマンスに重大な悪影響を与える可能性があります。
もう1つの一般的なC#の問題は、独自のコレクションオブジェクトを作成することです。それが決して適切ではないというわけではありませんが、.NETが提供するものと同じくらい包括的な選択により、車輪の再発明ではなく、既存のものを使用または拡張することで、おそらく多くの時間を節約できます。特に、C#およびCLI用のC5 Generic Collection Libraryは、永続的なツリーデータ構造、ヒープベースの優先度キュー、ハッシュインデックス付き配列リスト、リンクリストなど、「すぐに使える」さまざまな追加コレクションを提供します。
CLR環境はガベージコレクターを採用しているため、オブジェクト用に作成されたメモリを明示的に解放する必要はありません。実際、できません。 C ++に相当するものはありませんdelete
演算子またはfree()
Cの関数。しかし、それは、オブジェクトの使用を終えた後、すべてのオブジェクトを忘れることができるという意味ではありません。多くの種類のオブジェクトは、他の種類のシステムリソース(ディスクファイル、データベース接続、ネットワークソケットなど)をカプセル化します。これらのリソースを開いたままにしておくと、システムリソースの総数がすぐに枯渇し、パフォーマンスが低下し、最終的にプログラムの障害が発生する可能性があります。
デストラクタメソッドは任意のC#クラスで定義できますが、デストラクタ(C#ではファイナライザとも呼ばれます)の問題は、いつ呼び出されるかを確実に知ることができないことです。それらは、将来の不確定な時間にガベージコレクターによって(別のスレッドで呼び出され、追加の複雑さを引き起こす可能性があります)。 GC.Collect()
でガベージコレクションを強制することにより、これらの制限を回避しようとしていますではありません C#のベストプラクティス 、収集に適格なすべてのオブジェクトを収集している間、スレッドは不明な時間ブロックされます。
これは、ファイナライザーの適切な使用法がないということではありませんが、決定論的な方法でリソースを解放することはそれらの1つではありません。むしろ、ファイル、ネットワーク、またはデータベース接続を操作しているときは、基になるリソースを使い終わったらすぐに明示的に解放する必要があります。
ほとんどの場合、リソースリークが懸念されます 任意の環境 。ただし、C#は、堅牢で使いやすいメカニズムを提供します。このメカニズムを使用すると、リークが発生する可能性が非常に低くなります。 .NETフレームワークはIDisposable
を定義しますDispose()
のみで構成されるインターフェース方法。 IDisposable
を実装するオブジェクトオブジェクトのコンシューマーがオブジェクトの操作を終了するたびに、そのメソッドが呼び出されることを期待しています。これにより、明示的で決定論的なリソースの解放が実現します。
単一のコードブロックのコンテキスト内でオブジェクトを作成および破棄する場合、C#はDispose()
を提供するため、using
の呼び出しを忘れることは基本的に許されません。 Dispose()
を保証するステートメントコードブロックがどのように終了したかに関係なく(例外、returnステートメント、または単にブロックを閉じるかどうかに関係なく)呼び出されます。はい、それは同じですusing
ファイルの先頭にC#名前空間を含めるために使用される前述のステートメント。これには、多くのC#開発者が気付いていない、まったく関係のない2番目の目的があります。つまり、Dispose()
を確実にするためですコードブロックが終了すると、オブジェクトに対して呼び出されます。
using (FileStream myFile = File.OpenRead('foo.txt')) { myFile.Read(buffer, 0, 100); }
上記の例でusing
ブロックを作成することにより、myFile.Dispose()
が確実にわかります。 Read()
であるかどうかに関係なく、ファイルの処理が完了するとすぐに呼び出されます。例外をスローします。
C#は、実行時に型安全性の適用を継続します。これにより、C ++などの言語よりもはるかに迅速にC#の多くの種類のエラーを特定できます。この場合、型変換に誤りがあると、オブジェクトのフィールドに任意の値が割り当てられる可能性があります。ただし、繰り返しになりますが、プログラマーはこの優れた機能を浪費し、C#の問題を引き起こす可能性があります。 C#には、例外をスローできる方法とスローしない方法の2つの異なる方法が用意されているため、このトラップに陥ります。 try / catchブロックを作成する必要がないため、コーディングを節約できると考えて、例外ルートを敬遠する人もいます。
たとえば、C#で明示的な型キャストを実行する2つの異なる方法を次に示します。
次のうちどれが広告がどのように配置されるべきかを導く原則ですか?
// METHOD 1: // Throws an exception if account can't be cast to SavingsAccount SavingsAccount savingsAccount = (SavingsAccount)account; // METHOD 2: // Does NOT throw an exception if account can't be cast to // SavingsAccount; will just set savingsAccount to null instead SavingsAccount savingsAccount = account as SavingsAccount;
方法2の使用で発生する可能性のある最も明白なエラーは、戻り値のチェックの失敗です。その結果、最終的にNullReferenceExceptionが発生する可能性があります。これは、かなり後で発生する可能性があり、問題の原因を突き止めるのがはるかに困難になります。対照的に、方法1はすぐにInvalidCastException
をスローします。問題の原因をすぐに明らかにします。
さらに、メソッド2で戻り値を確認することを覚えていても、nullであることがわかった場合はどうしますか?あなたが書いている方法は、エラーを報告するのに適切な場所ですか?そのキャストが失敗した場合に試すことができる他の何かがありますか?そうでない場合は、例外をスローするのが正しいことなので、問題の原因のできるだけ近くで発生させることをお勧めします。
次に、一方が例外をスローし、もう一方が例外をスローしない、他の一般的なメソッドのペアの例をいくつか示します。
int.Parse(); // throws exception if argument can’t be parsed int.TryParse(); // returns a bool to denote whether parse succeeded IEnumerable.First(); // throws exception if sequence is empty IEnumerable.FirstOrDefault(); // returns null/default value if sequence is empty
一部のC#開発者は「例外に不利」であるため、例外をスローしない方法が優れていると自動的に想定します。これが当てはまる特定のケースがいくつかありますが、一般化としてはまったく正しくありません。
具体的な例として、例外が生成された場合に実行する代替の正当な(デフォルトなどの)アクションがある場合、例外ではないアプローチが正当な選択である可能性があります。そのような場合、実際には次のようなものを書く方が良いかもしれません:
if (int.TryParse(myString, out myInt)) { // use myInt } else { // use default value }
の代わりに:
try { myInt = int.Parse(myString); // use myInt } catch (FormatException) { // use default value }
ただし、TryParse
と仮定するのは誤りです。したがって、必然的に「より良い」方法です。そうなることもあれば、そうでないこともあります。そのため、2つの方法があります。例外は確かに開発者としてのあなたの友達になる可能性があることを忘れないで、あなたがいるコンテキストに合ったものを使用してください。
この問題はC#固有のものではありませんが、C#コンパイラーが提供する厳密な型チェックの利点を放棄するため、C#プログラミングでは特に深刻です。
警告は理由のために生成されます。すべてのC#コンパイラエラーはコードの欠陥を示していますが、多くの警告も同様です。この2つを区別するのは、警告が発生した場合、コンパイラーはコードが表す命令を問題なく出力できることです。それでも、コードが少し怪しいと感じ、コードが意図を正確に反映していない可能性があります。
このC#プログラミングチュートリアルの一般的な簡単な例は、使用していた変数の使用を排除するようにアルゴリズムを変更したが、変数宣言を削除するのを忘れた場合です。プログラムは完全に実行されますが、コンパイラーは役に立たない変数宣言にフラグを立てます。プログラムが完全に実行されるという事実により、プログラマーは警告の原因を修正することを怠ります。さらに、コーダーはVisual Studioの機能を利用して、「エラーリスト」ウィンドウで警告を簡単に非表示にして、エラーのみに集中できるようにします。数十の警告が表示されるまで、それほど時間はかかりません。すべての警告は、幸いにも無視されます(さらに悪いことに、非表示になります)。
しかし、遅かれ早かれ、このタイプの警告を無視すると、次のようなものがコードに組み込まれる可能性があります。
class Account { int myId; int Id; // compiler warned you about this, but you didn’t listen! // Constructor Account(int id) { this.myId = Id; // OOPS! } }
そして、Intellisenseがコードを記述できる速度で、このエラーは見た目ほど起こりそうにありません。
これで、プログラムに重大なエラーが発生しました(コンパイラーは、すでに説明した理由により、警告としてフラグを立てただけですが)。プログラムの複雑さによっては、これを追跡するのに多くの時間を浪費する可能性があります。そもそもこの警告に注意を払っていれば、5秒の簡単な修正でこの問題を回避できたでしょう。
C Sharpコンパイラは、コードの堅牢性に関する多くの有用な情報を提供することを忘れないでください…聞いている場合。警告を無視しないでください。 通常、修正には数秒しかかかりません。新しいものが発生したときに修正すると、時間を節約できます。 Visual Studioの「エラーリスト」ウィンドウに「0エラー、0警告」が表示されることを期待するようにトレーニングしてください。そうすれば、警告があるとすぐに対処するのに十分な不快感を覚えます。
もちろん、すべてのルールには例外があります。したがって、コードが意図したとおりであるにもかかわらず、コードがコンパイラーにとって少し怪しげに見える場合があります。非常にまれなケースでは、#pragma warning disable [warning id]
を使用してください警告をトリガーするコードのみ、およびそれがトリガーする警告IDのみ。これにより、その警告とその警告のみが抑制されるため、新しい警告に引き続き注意を払うことができます。
C#は、生産性を大幅に向上させることができる多くのメカニズムとパラダイムを備えた強力で柔軟な言語です。ただし、他のソフトウェアツールや言語と同様に、その機能の理解や認識が限られていると、メリットよりも障害になることがあり、「危険を十分に理解している」ということわざの状態になります。
このようなCシャープチュートリアルを使用して 慣れ親しむ この記事で提起された問題など、C#の重要なニュアンス(ただし、これに限定されない)を使用すると、言語のより一般的な落とし穴を回避しながら、C#の最適化に役立ちます。
関連: 6つの重要なC#インタビューの質問C#は、Microsoft CLRを対象とするいくつかのプログラミング言語の1つであり、言語間の統合と例外処理、強化されたセキュリティ、コンポーネント相互作用の簡略化されたモデル、デバッグおよびプロファイリングサービスの利点を自動的に提供します。
C ++とC#は、名前と構文が類似しているにもかかわらず、2つの完全に異なる言語です。 C#はC ++よりもいくらか高レベルになるように設計されており、2つの言語は、パラメーターが参照によって渡されるか値によって渡されるかを誰が決定するかなど、詳細に対して異なるアプローチを取ります。
C#はさまざまな理由で使用されますが、Microsoft CLRツールセットと大規模な開発者コミュニティの利点は、この言語の2つの主な魅力です。