前のセクションでは、内容がタイプに合わせて、残りのこのブログへの導入後、演算子オーバーロードとどのようにオブジェクトのメソッドを書き換え、とします:
アセンブリ参照と名前空間の定義
これらの比較的単純なコンテンツのいくつかは、あまりにも多く、たとえば、3つだけの引用の方法に注意を払う必要がある他のアセンブリを参照、記述されません。
- 最初の方法は、参照ライブラリのプロジェクトファイルで、プロジェクトのソースコードのライブラリという二つのプロジェクト間の依存関係を確立指摘。プログラムにライブラリをコンパイルするには良いリファレンスライブラリをコンパイルした後。依存関係は、コンパイラは、ライブラリをコンパイルするときに生じる(あなたが任意のコンパイルされていない場合)されたプロジェクトのコピーを、共通
- 第二の方法は、ファイル自体のアセンブリ参照されています。言い換えれば、代わりにコンパイルされたプログラムのリファレンスライブラリ。会社内の別のチームがまとめたようなライブラリやプログラムは、別々にコンパイルする場合は、このアプローチは非常に合理的です。(DLLのコピー、との時間をデバッグします)
- 第三の方法は、参照NuGetパッケージです(プロジェクトアドバンストコピー、ライブラリNuGetからアイテムをコピー)
注ライブラリとパッケージは、コンソールプログラムによって参照されていないだけ。実際には、任意のアセンブリは他のアセンブリを参照することができます。多くの場合、ライブラリの参照別のライブラリは、依存関係のチェーンを作成します。:種類の下でアクセスのレベルを懸念しながら
注:アクセスのメンバーが、その外側のクラスよりも大きくすることはできません、(任意の修飾子を使用することができ、ネストされたクラスを除く)のクラスは唯一の公共および内部使用することができます。名前空間には、ライン上で、次の設計仕様に注意を払うに比較的単純な、唯一の必要性です:
- 名前空間接頭辞追加の会社名と同じ名前を使用して異なる企業を防止します。
- するには、名前空間の安定した二名を使用して、製品名のアップグレードバージョンを変更しないでください。
- 明示的に名前空間にタイプを定義しないでください。
- スペースを作成することを検討し、ファイル階層マッチングのフォルダ構造に名前を付けます。
すべてのすべてでは、名前空間を持つファイルで最高のクラスは、クラスをラップするということです。
XMLコメント
C#コンパイラは、結果の実行可能ファイル内のすべてのコメントを無視しますが、開発者が使用できますが、コマンドラインオプションは、コンパイラは別のXMLファイルにXMLコメントを抽出指示します。これは、XMLコメントからAPIドキュメントを生成します。また、C#のエディタは、コード内のXMLコメントを解析することができ、XMLコメント表示現像を解析する領域表示(例えば、他のコードからの色別を使用して)、またはデータ要素を行いました。時間あなたが標準の動作を文書化することができるように、例えば、APIドキュメントを解決するために生成するパラメータを定義
ガーベジコレクション
ガベージコレクションは、コアは、もはや参照されるメモリが占有再利用オブジェクトに設計された機能「実行中」されます。**この文は、「メモリ」と「参照」に焦点を当てています。再利用メモリにガベージコレクタのみ、他のリソースを扱っていないデータベース接続など、ハンドル(シリアルポートなど)(ファイル、ウィンドウ、など)、ネットワークポート、およびハードウェアデバイス。また、ガベージコレクタは、すべての参照があるかどうかに応じて、あなたがきれいにしたいかを決定します。これは、ことを意味するガベージコレクタハンドルは再利用のみヒープメモリに、オブジェクトへの参照です。加えて、手段は、オブジェクトへの参照を維持する場合、ガベージコレクタはオブジェクトが使用するメモリの再使用を防止します。**
ガベージコレクションのアルゴリズム
次のように.NET使用してマーク及びコンパクトアルゴリズムはメモリ内のオブジェクトの整理およびアクセス不能オブジェクトを覆うように設置し、その後クリーンアップを実行し、全体の操作プロセスが到達可能性アルゴリズムを使用して、次のとおりです。
- ガベージコレクションサイクルの開始時に、全てのルートオブジェクトを識別します。ルート静的変数、CPUレジスタと(F-到達可能なオブジェクトを含む)は、ローカル変数またはパラメータ例からの参照任意の参照リストに基づいて、ガベージコレクタは、参照することによって識別された各ツリー構造のルートを通過し、再帰的にすべてのルートを決定することができますオブジェクトの基準点。これにより、ガベージコレクタは、すべてのオブジェクトが到達可能で識別することができます。
- 执行垃圾回收时,垃圾回收器不是枚举所有访问不到的对象;相反,它将所有可达对象紧挨着放到一起,从而覆盖不可访问的对象(也就是垃圾,或者不可达对象)所占用的内存,为定位和移动所有可达对象,系统要在垃圾回收器运行期间维持状态的一致性。为此,进程中的所有托管线程都会在垃圾回收期间暂停。这显然会造成应用程序出现短暂的停顿。不过,除非垃圾回收周期特别长,否则这个停顿是不太引人注意的。为尽量避免在不恰当的时间执行垃圾回收,System.GC对象包含一个**Collect()**方法。可在执行关键代码之前调用它(执行这些代码时不希望GC运行)。这样做不会阻止垃圾回收器运行,但会显著减小它运行的可能性——前提是关键代码执行期间不会发生内存被大量消耗的情况
- 发现相较于长期存在的对象,最近创建的对象更有可能需要垃圾回收。为此,.NET垃圾回收器支持“代”(generation)的概念,它会以更快的频率尝试清除生存时间较短的对象(新生对象)。而那些已在一次垃圾回收中“存活”下来的对象(老对象)会以较低的频率清除。具体地说,共有3代对象。一个对象每次在一个垃圾回收周期中存活下来,它都会移动到下一代,直至最终移动到第二代(从第零代开始)。相较于第二代对象,垃圾回收器会以更快的频率对第零代的对象执行垃圾回收
相较于Java的成体系的垃圾回收算法,感觉CLR做的不是很好,也许是没有体系化的了解过吧。
弱引用
弱引用是什么?弱引用就是一直维持的一个引用,但是它并不会组织垃圾回收,为什么要有弱引用呢?这么去想象一下,假如要从数据库加载一个特别大的对象,如果是强引用,一旦用户不再使用该对象,断开引用就需要进行垃圾回收,但假如用户一会儿又想用了,又得重新加载和引用,但如果是弱引用,你可以在保持引用的状态下进行垃圾回收,但在你垃圾回收之前我还是一直保持引用状态,那么假如用户下次请求的时候刚好CLR还没有回收对象,就可以直接获取到对象了,实际上相当于一次内存里的缓存。
public class Program
{
private WeakReference Data;
public FileStream GetData()
{
FileStream data = (FileStream)Data.Target;
if(data != null)
{
return data;
}
else
{
// Load data
// ...
// Create a weak reference
// to data for use later
Data.Target = data;
}
return data;
}
public static void Main()
{
Console.WriteLine("No output in this example.");
}
}
创建弱引用(Data)之后,可查看弱引用是否为null来检查垃圾回收。但这里的关键是先将弱引用赋给一个强引用(FileStream data=Data),避免在“检查null值”和“访问数据”这两个动作之间,垃圾回收器运行并清除弱引用。强引用明显会阻止垃圾回收器清除对象,所以它必须先被赋值(而不是先检查Target是不是为null)
终结器
我们知道垃圾回收器不负责处理除了堆上的引用资源,那么除了内存管理外的一些数据库连接以及句柄等这些资源该怎么释放呢,这个时候就要用到终结器,同时终结器在垃圾回收前也发挥着不可替代的作用(虽然会降低性能,但是能保证对象被延迟清除):
class TemporaryFileStream
{
public TemporaryFileStream(string fileName)
{
File = new FileInfo(fileName);
Stream = new FileStream(
File.FullName, FileMode.OpenOrCreate,
FileAccess.ReadWrite);
}
public TemporaryFileStream()
: this(Path.GetTempFileName())
{ }
// Finalizer
~TemporaryFileStream()
{
Close();
}
public FileStream Stream { get; }
public FileInfo File { get; }
public void Close()
{
Stream?.Dispose();
File?.Delete();
}
}
}
上边这个终结器用来删除文件和关闭流。需要注意的几点是:
- 终结器不能被显式调用,只能通过垃圾回收器调用,会在对象最后一次使用之后调用,开发人员只能定义不能调用
- 终结器不允许传递任何参数,不可重载,不可添加访问修饰符
这里的dispose一个对象不是垃圾回收一个对象,而是释放该对象中包装的资源,例如它字段所引用的对象,解除引用,具体的垃圾回收还是需要垃圾回收器的。这里有个漏洞会导致Dispose()执行不了,也就是在实例化TemporaryFileStream后,调用Dispose()前如果发生异常可能不会调用Dispose(),这个时候就要使用确定性终结:
public static void Search()
{
using(TemporaryFileStream fileStream1 =
new TemporaryFileStream(),
fileStream2 = new TemporaryFileStream())
{
// Use temporary file stream;
}
}
相当于包裹了一个try finally块。所以实现了终结器的方法的对象的垃圾回收流程是这样的(如果终结器不调用System.GC.SuppressFinalize):整体流程是:先由可达性分析算法把待清理的对象引用放到f-reachable队列里,然后由终结器进行引用终结,终结完成后垃圾回收器才对对象进行垃圾回收。需要注意以下几点:
- 要只为使用了稀缺或昂贵资源的对象实现终结器方法,即使终结会推迟垃圾回收。
- 要为有终结器的类实现IDisposable接口以支持确定性终结。
- 要为实现了IDisposable的类实现终结器方法,以防Dispose()没有被显式调用。
- 要重构终结器方法来调用与IDisposable相同的代码,可能就是调用一下Dispose()方法。
- 不要在终结器方法中抛出异常。
- 要从Dispose()中调用System.GC.SuppressFinalize(),使垃圾回收更快地发生,并避免重复性的资源清理。
- 要保证Dispose()可以重入(可被多次调用)。
- 要保持Dispose()的简单性,把重点放在终结所要求的资源清理上。
- 避免为自己拥有的、带终结器的对象调用Dispose()。相反,依赖终结队列清理实例。
- 最後に、他のオブジェクトへの参照を避けるプロセスの終わりではない(被写体復活、F-到達可能なキューが外部参照されています)。
- 処分を書き換える場合)(基本クラスの実装を呼び出します。
- オブジェクトが廃棄()状態を呼び出した後に設定されて考えてみては使用できません。オブジェクトが廃棄された後、廃棄を呼び出すことに加えて()メソッドは、外側説明ObjectDisposedExceptionをスローする。例外。(廃棄()を複数回呼び出す必要があります。)
- 実装IDisposableインターフェイスそのタイプのフィールド(または属性)を含む廃棄、及びこれらのフィールドを参照しているオブジェクトを配置します
その後、私たちはより良い必要が、それは良いことではない初期化するために再び生じたときに彼を聞かせていた、オブジェクトはプロセスを通過します回収されるか、複雑見ることができます。
using AddisonWesley.Michaelis.EssentialCSharp.Chapter10.Listing10_21;
class DataCache
{
// ...
public TemporaryFileStream FileStream
{
get
{
if (_FileStream == null)
{
_FileStream = new TemporaryFileStream();
}
return _FileStream;
}
}
private TemporaryFileStream _FileStream = null;
// ...
}
TemporaryFileStreamロードされたときにのみ、FileStreamを属性のgetメソッドを呼び出します。
アセンブリがロードされている方法であること、実際には、要約すると、ガベージコレクションのターミネータ(デストラクタ)、そのメリットとデメリットを実行する方法だけでなく、名前空間の仕様を使用しています。