C#およびCLRに関する興味深いメモ



C#プログラミング言語を勉強すると、言語自体とそのランタイムの両方の機能に出くわしました。その一部は、いわば「狭い円で広く知られています」。 正直に言って今までやったことのないことを繰り返すために、毎日彼の貯金箱に集めて、共有するというアイデアを得ました。

これらの注意事項は、コードをより美しく、より速く、より信頼できるものにするものではありません。これにはSteve McConnellがいます。 しかし、彼らは間違いなくあなたの考え方と理解に貢献します。

以下のいくつかは単純すぎるように見えますが、別のものは逆に難しく、必要ではありませんが、それはありません。

だから、私たちは始めます:

1)動的メモリ内のオブジェクトとインスタンスの場所


オブジェクトには、静的フィールドとすべてのメソッドが含まれます。 インスタンスには、非静的フィールドのみが含まれます。 これは、メソッドが各インスタンスで複製されず、Flyweightパターンがここで使用されることを意味します。

2)メソッドにパラメーターを渡す


構造体はそのコピーをメソッドに渡し、クラスはその参照のコピーを渡します。 しかし、REFキーワードを使用すると、構造体はそれ自体へのポインターを渡し、クラスは元のリンクを渡します。
 REF   . 1)   ,     . static void Main( string[] args ) { StringBuilder sb = new StringBuilder(); sb.Append("Hello "); AppendHello(sb); Console.WriteLine(sb.ToString()); } private static void AppendHello(StringBuilder sb) { sb.Append(" World!"); sb = null; } 2)   System.NullReferenceException          null. static void Main( string[] args ) { StringBuilder sb = new StringBuilder(); sb.Append("Hello "); AppendHello(ref sb); Console.WriteLine(sb.ToString()); } private static void AppendHello(ref StringBuilder sb) { sb.Append(" World!"); sb = null; } 


3)実行前にコードを準備する


CLRにはCERブロックがあり、これはJITに「実行前にコードを準備するので、必要なときにすべてが手元にある」ことを伝えます。 これを行うには、System.Runtime.CompilerServicesおよびRuntimeHelpers.PrepareConstrainedRegions名前空間を接続します。

4)正規表現


RegexはCompiledオプションで作成できます-これにより、ILコードで式が生成されます。 通常よりはるかに高速ですが、最初の起動は遅くなります。

5)配列


ILの1次元配列はベクトルで表され、多次元配列よりも高速に動作します。 1次元配列の配列はベクトルを使用します。

6)コレクション


ICollectionからカスタムコレクションを継承することをお勧めします。IEnumerableの実装は無料です。 しかし、インデックスはありません(非常に個人的な)。

7)広範な方法


拡張メソッドの名前が型メソッドの名前と競合する場合、拡張メソッドの完全な名前を使用でき、型を引数として渡すことができます。

 StaticClass.ExtesionMethod( type ); 

8)LINQ


LINQ遅延読み込み-選択、場所、取得、スキップなど
LINQ eager loading(リクエストはすぐに実行されます)-カウント、平均、最小、最大、ToListなど (ただし、コレクションが無限の場合、リクエストは終了しません。)

9)同期ブロック


構造型とプリミティブ(byte、int、long ...)には同期ブロックがありません。同期ブロックは、リンクとともにマネージヒープ内のオブジェクトに存在します。 したがって、Monitor。()またはLock()構成は機能しません。

10)インターフェース


C#で、このメソッドが定義されているインターフェイスの名前(IDisposable.Dispose)がメソッド名の前に示されている場合、インターフェイスメソッドの明示的な実装(明示的なインターフェイスメソッド実装、EIMI)を作成します。 C#でインターフェイスメソッドを明示的に実装する場合、アクセスレベル(オープンまたはクローズ)を指定することはできません。 ただし、コンパイラはメソッドのメタデータを作成するときに、プライベートアクセスレベルを割り当てます。これにより、インターフェイスメソッドを呼び出すだけで、コードがクラスインスタンスを使用できなくなります。 インターフェイスメソッドを呼び出す唯一の方法は、このインターフェイスタイプの変数を介してアクセスすることです。

EIMIは不可欠です(たとえば、同じ名前と署名を持つ2つのインターフェイスメソッドを実装する場合)。

11)C#ではありませんが、ILはサポートされています


インターフェイスの静的フィールド、戻り値のみが異なるメソッドなど。

12)シリアル化


オブジェクトのグラフをシリアル化する場合、シリアル化できるタイプとできないタイプがあります。 パフォーマンス上の理由から、書式設定モジュールは、シリアル化の前にすべてのオブジェクトに対してこの操作の可能性をチェックしません。 これは、SerializationExceptionが発生する前に、一部のオブジェクトがストリームにシリアル化されるときに状況が発生する可能性があることを意味します。 結果として、破損したデータはI / Oストリームにあります。 これは、たとえば、MemoryStreamで最初にオブジェクトをシリアル化することで回避できます。

C#では、[Serializable]属性でマークされた型の内部では、自動的に実装されるプロパティを定義しないでください。 実際には、コンパイラによって生成されるフィールド名は、後続の各コンパイル後に変更される可能性があるため、型インスタンスをデシリアライズできなくなります。

13)定数


定数はアセンブリメタデータに配置されるため、変更があった場合は、それを使用してすべてのアセンブリを再コンパイルする必要があります。 なぜなら 定数を持つDLLはロードされない場合があります。
コンストラクターで静的な読み取り専用の設定値を使用することをお勧めします。これを使用してアセンブリに常にロードされ、実際の値を返します。

14)代表者


GetInvocationList-デリゲートのチェーンを返します。最後の呼び出しだけでなく、anyを呼び出して例外をキャッチし、すべての戻り値を取得できます。

15)文字列の比較


Microsoft Windowsでは、大文字の文字列比較が最適化されています。 *実際、StringComparison.OrdinalIgnoreCaseはCharを大文字に変換します。 ToUpperInvariant。 string.compare()を使用します。 WindowsはデフォルトでUTF-16エンコードを使用します。

16)複数行の最適化


アプリケーションで文字列を大文字と小文字を区別して比較することが多い場合、またはアプリケーションに多くの同一の文字列オブジェクトが表示されることが予想される場合は、パフォーマンスを向上させるために、CLRでサポートされる文字列抑制メカニズムを使用する必要があります。

初期化時に、CLRはキーが文字列で値が管理ヒープ内の文字列オブジェクトへの参照である内部ハッシュテーブルを作成します。

17)安全なひも


SecureStringオブジェクトを作成するとき、そのコードは、文字の配列を含むアンマネージメモリのブロックを割り当てます。 ガベージコレクターは、このアンマネージメモリについて何も知りません。 このメモリは手動でクリアする必要があります。

18)セキュリティ


マネージアセンブリは、常にDEPとASLRを使用します。

19)メソッド設計


メソッドパラメータの型を宣言するときは、基本クラスへのインターフェイスを優先して、「最小」型を指定する必要があります。 たとえば、一連の要素で機能するメソッドを作成する場合、IEnumerableインターフェイスを使用してメソッドパラメーターを宣言することをお勧めします。

 public void ManipulateItems<T>(IEnumerable<T> collection) { ... } 

同時に、メソッドによって返されたオブジェクトのタイプを宣言し、利用可能なオプションの中で最も強力なものを選択することが望ましいです(特定のタイプに制限されないようにしてください)。 たとえば、StreamではなくFileStreamオブジェクトを返すメソッドを宣言することをお勧めします。

20)車の特性についてもう一度


AIPの自動的に実装されたプロパティを使用しないことをお勧めします(著者の意見、どちらを推測するか)。
a)デフォルト値は、コンストラクターでのみ設定できます。 (Roslyn C#6で変更);
b)シリアル化の問題(段落12)。
c)ブレークポイントを設定できません。

21)設定ファイル


a)任意のバイナリ.NETコードを外部XML構成ファイルに関連付けることができます。 このファイルは同じディレクトリにあり、末尾に.CONFIGという単語が追加された同じ名前です。
b)ソリューションをバイナリ形式でのみ提供する場合、ドキュメント化コメントはコンパイル中にXMLファイルにコンパイルできるため、この状況でも、優れたヒントをユーザーに提供できます。 これを行うには、結果のXMLファイルをバイナリと同じディレクトリに配置するだけで、Visual Studio .NETはIntelliSenseプロンプトにコメントを自動的に表示します。

22)例外


CLRは、例外の開始点をリセットします。

 try {} catch (Exception e) { throw e; } 

CLRは、例外の開始点に関する情報を変更しません。

 try {} catch (Exception e) { throw; } 

CLRがハンドラーの検索を開始する前に、AppDomainクラスのFirstChanceExceptionイベントを発生させ、例外に関する情報を受け取ることができます。

例外はゆっくり動作します カーネルモードへの移行があります。

23)ISおよびAS


IS-このコードでは、CLRはオブジェクトを2回チェックします。

 if ( obj is Person ) { Person p = (Person) obj; } 

AS-この場合、CLRはobjのPerson型との互換性を1回だけチェックします。

 Person p1 = obj as Person; if ( p1 != null ) { ... } 

24)実行する前に十分なメモリがあるかどうかを確認します


MemoryFailPointクラスのインスタンスを作成すると、アクションを開始する前、または例外をスローする前に、十分なメモリがあるかどうかが確認されます。 ただし、物理メモリはまだ割り当てられていないことに注意してください。このクラスは、アルゴリズムが必要なメモリを受け取ることを保証できません。 しかし、その使用は間違いなくアプリケーションの信頼性を高めるのに役立ちます。

25)Nullについて


null互換のInt32を使用するには、次のように記述できます。

 Nullable<Int32> x = null;  Int32? x = null; 

null互換値の結合演算子は?? (左のオペランドがnullの場合、演算子は次のオペランドに進みます)、2つの同等の式を検討します。
1)
 string temp = GetFileName(); string fileName = ( temp != null ) ? temp : "Untitled"; 

2)
 string fileName = GetFileName() ?? "Untitled"; 


26)タイマー


FCLライブラリには、さまざまなタイマーが含まれています。
1)System.Threadingのタイマー-プールスレッドでバックグラウンドタスクを実行するのに適しています。
2)System.Windows.Formsのタイマー-タイマーは呼び出しスレッドに関連付けられています。これにより、並列呼び出しが防止されます。
3)System.Windows.ThreadingのDispatcherTimer。 -2番目と同等ですが、SilverlightおよびWPFアプリケーション用。
4)System.Timersのタイマー。 -実際、最初のシェルであるため、Jeffrey Richterはそれを使用することを推奨していません。

27)typeおよびtypeof


型のTypeインスタンスを取得するには、Type.GetTypeメソッドの代わりにtypeof操作が使用されます。 コンパイル時に型がわかっている場合、typeof操作は実行時にメソッドを実行するのではなく、すぐにメソッドを検索します。

28)チップを使用する


コードの量を減らしてより明確にするために? 次のようにusingディレクティブを使用できます。
使用DateTimeList = System.Collections.Generic.List <System.DateTime>;

29)プリプロセッサディレクティブ


a)#IF DEBUGおよび#ENDIF-DEBUGモードでのみコンパイルされるコードブロックを示すために使用されます。
b)#DEFINE XXX; #IF(DEBUG && XXX)-アセンブリ番号「XXX」を条件に追加できます。
c)!DEBUG == RELEASE(念のため)。
d)#LINE 111-エラーウィンドウに111行目が表示されます。
e)#LINE HIDDEN-デバッガーから行を非表示にします。
f)#WARNING XXX; #ERROR YYY-XXX-警告、YYY-エラーを意味します。

30)任意*古さ


共分散-直接順の変換、キーワードOUT。

 string[] strings = new string[3]; object[] objects = strings; interface IMyEnumerator<out T> { T GetItem( int index ); } T --> R IOperation<T> --> IOperation<R> 

反分散-逆順の変換、キーワードIN。

 interface IMyCollection<in T> { void AddItem( T item ); } R --> T IOperation<T> --> IOperation<R> 

不変性-暗黙的な型変換は許可されていません。

デフォルトでは、ジェネリック型は不変です。 まだ一般化されたクラスはオープンと呼ばれ、実行時には、たとえば特定のタイプの「int」、「string」によってクローズされます。 そして、これらは異なるタイプであり、それらの静的フィールドも異なります。

31)拡張メソッド


 public static class StringBuilderExtensions { public static Int32 IndexOf ( this StringBuilder sb, Char char) { ... } } 

インスタンスメソッド構文によって呼び出される静的メソッドを定義できます。 たとえば、StringBuilderに対して独自のIndexOfメソッドを定義できます。 まず、コンパイラーはStringBuilderクラスまたはそのすべての基本クラスを調べて、必要なパラメーターを持つIndexOfメソッドが存在するかどうかを確認します。見つからない場合は、特定のIndexOfメソッドを持つ静的クラスを探します。

そして、これは.NetのVisitorパターンの実装です。

32)実行コンテキスト


各スレッドには特定の実行コンテキストがあります。 これには、セキュリティ設定、ホストパラメーター、および論理呼び出しコンテキストデータが含まれます。 デフォルトでは、CLRは最初のスレッドからすべての補助スレッドに自動的にコピーします。 これにより、同じセキュリティ設定が保証されますが、パフォーマンスが低下します。 このプロセスを制御するには、ExecutionContextクラスを使用します。

33)揮発性


JIT-コンパイラは、このキーワードでマークされたフィールドへのアクセスが、揮発性の読み取りまたは書き込みモードで発生することを保証します。 C#コンパイラーとJITコンパイラーがフィールドの内容をプロセッサー・レジスターにキャッシュすることを禁止します。これにより、すべての読み取りおよび書き込み操作中に、メモリーで操作が直接実行されることが保証されます。

34)並列スレッド処理のコレクションクラス


ConcurrentQueue-FIFOアルゴリズムに従って処理要素。
ConcurrentStack-LIFOアルゴリズムを使用した処理要素。
ConcurrentBag-重複を許可するソートされていない要素のセット。
ConcurrentDictionary <TKey、TValue>-キーと値のペアのソートされていないセット。

35)ストリーム


ユーザーモードの設計:
a)揮発性構造-アトミックな読み取りまたは書き込み操作。
VolatileWrite、VolatileRead、MemoryBarrier。
b)インターロック構造-アトミックな読み取りまたは書き込み操作。
System.Threading.Interlocked(インターロック)およびSystem.Threading.SpinLock(ループでロック)。
どちらの構成でも、リンク(メモリ内のアドレス)を変数に渡す(構造を呼び出す)必要があります。

カーネルモードの構成:
ユーザーモードの構築よりも約80倍遅くなりますが、MSDNで説明されている多くの利点があります(多くのテキスト)。

クラス階層:

 WaitHandle EventWaitHandle AutoResetEvent ManualResetEvent Semaphore Mutex 


36)クラスフィールド


フィールドを明示的に初期化せず、デフォルトのコンストラクターでこれを行い、引数を取るコンストラクターに対してthis()キーワードとともに使用します。 これにより、コンパイラが生成するILコードが少なくなります。 初期化はいずれかのコンストラクターで1回発生します(すべてのコンストラクターが同じ値、コピーを持つわけではありません)。

37)プログラムのコピーを1つだけ実行する


 public static void Main () { bool IsExist; using ( new Semaphore ( 0, 1, "MyAppUniqueString", out IsExist ) ) { if ( IsExist ) { /*    ,      . */ } else { /*         ,   ,      Main,      . */ } }} 


38)ガベージコレクション


CLRは2つのモードを実装します。
1)ワークステーション-コレクターは、他のアプリケーションがプロセッサーリソースを使用しないと仮定します。 モード-並列アセンブリの有無にかかわらず。
2)サーバーコレクターは、サードパーティのアプリケーションがマシン上で実行されておらず、すべてのCPUリソースがアセンブリのために実行されていることを前提としています! マネージヒープは、プロセッサごとに1つのセクションに分割されます(すべての結果、つまり、ヒープごとに1つのスレッド)。

39)ファイナライザー


削除前のオブジェクトの最後の欲求の機能を満たし、40秒より長く持続することはできません。それで遊んではいけません。 オブジェクトを少なくとも1世代に変換します。 すぐには削除されません。

40)施設でのガベージコレクタの監視と管理


GCHandleオブジェクトの静的Allocメソッドを呼び出し、オブジェクトへの参照を渡し、GCHandleTypeを入力します。
1)弱-監視、オブジェクトが使用できなくなったことがわかり、ファイナライザを実行できた。
2)WeakTrackResurrection-監視、オブジェクトが利用できなくなっていることがわかり、ファイナライザーが実行された場合(存在する場合)。
3)通常-コントロール。オブジェクトをメモリに残します。このオブジェクトが占有するメモリは圧縮できます。
4)固定-コントロール。オブジェクトをメモリに残します。このオブジェクトが占有するメモリは圧縮(移動)できません。

41)CLR


CLRは、本質的にMSIL命令用のプロセッサです。 従来のプロセッサはレジスタとスタックを使用してすべての命令を実行していましたが、CLRはスタックのみを使用します。

42)再帰


アプリケーションがさらに1秒間停止し、エラーメッセージなしで完全に消えたのを見たことがあれば、それはほぼ間違いなく無限の再帰が原因でした。 ご存じのとおり、スタックオーバーフローはインターセプトおよび処理できません。 なんで? 本やブログを読みます。

43)WindbgとSOS(Son of Strike)


プロセスには一度にいくつのドメインが存在しますか?
-3.システムドメイン、共有ドメイン、およびドメイン1(現在のアプリケーションコードを持つドメイン)。

実際にはいくつのヒープ(世代)がありますか?
-0、1、2、およびラージオブジェクトヒープ。
ラージオブジェクトヒープ-非常に大きなオブジェクトの場合、デフォルトでは圧縮されず、構成XMLファイル内の構成によってのみ圧縮されます。

ガベージコレクションのクライアントモードとサーバーモードのもう1つの違い(書籍ではすべてが詳細であるとは限らず、翻訳の不正確さが可能です)。
- 各コアに対して、独自のHEAPが作成され、それぞれに独自の0、1、2世代およびラージオブジェクトヒープがあります。

64ビットプラットフォームで2 GBを超えるアレイを作成する。
-gcAllowVeryLargeObjects enabled = "true | false"

空きメモリはあるが、新しいオブジェクトに大きな連続セクションを割り当てることができない場合はどうすればよいですか?
- ラージオブジェクトヒープのコンパクトモードを有効にします。 GCSettings.LargeObjectHeapCompactionMode;
使用することは推奨されません。メモリ内の大きなオブジェクトを移動するのは非常に高価です。

実行時にスレッドループ(デッドロック)を検出する速度はどれくらいですか?
-!Dlk

ソース(広告ではない):
1)Jeffrey Richter、「C#を介したCLR」第3版/第4版。
2)Trey Nash、「プロフェッショナル向けのC#2010クラッシュコース」。
3)John Robbins、「Microsoft .NETおよびMicrosoft Windows用のアプリケーションのデバッグ」。
4)Alexander Shevchuk(MCTS、MCPD、MCT)およびOleg Kulygin(MCTS、MCPD、MCT)ITVDNリソース(https://www.youtube.com/user/CBSystematicsTV/videos?shelf_id=4&view=0&sort=dd)。
5)セルゲイ・プガチョフ。 Microsoftエンジニア(https://www.youtube.com/watch?v=XN8V9GURs6o)

このリストが初心者と経験豊富なC#プログラマの両方に楽しんでいただければ幸いです。

ご清聴ありがとうございました!

*更新されたエラーを修正し、いくつかのポイントに例を追加しました。
間違いを見つけた場合は、個人的なメッセージでこれを報告してください。

Source: https://habr.com/ru/post/J256819/


All Articles