熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

完成器(Finalizer)、程序集名、方法信息

2022-06-13   來源: .NET編程 
問題: 在我的類中何時需要實現一個完成器?我是否一定要實現完成器或者只是在我控制著 非托管資源時才需要實現它?我是否一定要在我的完成器中實現 IDisposable 接口?反之又是如何的呢?

答案: 完成器只是在你控制了需要被清除的資源的才需要實現舉個例子FileStream 控制了一個本地的文件句柄並且實現了一個完成器這樣也就保證了 FileStream 被垃圾回收器回收時能釋放這個句柄不幸的是完成器給垃圾回收帶來了一定意義上的負擔因此應僅在必要時才使用完成器
  IDisposable 接口的實現表明你的類控制了需要被釋放的資源並且允許你的類用戶決定是否要釋放它們因此任何一個類實現了完成器就一定實現了 IDisposable 接口(如果垃圾回收器能自動釋放資源那麼也應該允許開發者顯式地調用某個方法來完成同樣的工作)但這並不是絕對的並不是所有實現了IDisposable接口的類都要實現一個完成器
   設想一下我的托管類有一個FileStream類型的私有成員FileStream 控制了一個非托管的資源並且實現了 IDisposable 接口和完成器當對該實例不再有引用時FileStream就變成無法訪問的與可終結的了對我的類而言它沒有理由在注冊對象隊列裡等待終結因為內嵌的FileStream的實例已經被注冊了另一方面考慮到我的類應該為用戶提供方法以立即釋放它所控制的資源無論這種方法是直接地還是間接的因此我的類應當實現IDisposable接口我的Dispose()實現很簡單它只是簡單地調用了FileStream的Dispose()方法切記盡管如此你也需要特別小心地釋放共享資源(比如正被別的實例使用的資源)如果你編寫一個類從外部釋放資源而使該資源可用請確保你的文檔在這個主題上是清晰明確的這樣其他人就知道是否正在移交他們給你的那些資源的控制權了
   如果你的類不需要實現完成器在你的Dispose()方法中應當調用GCSuppressFinalize()方法以確保系統不會去調用你的實例的完成器這樣你的實例同時也會從待終結對象集合中被刪除從而減輕了垃圾回收器在回收過程中的負擔貫穿於Microsoft NET Framework中常見的實現模式是給Dispose()方法添加一個Boolean(邏輯)類型的參數這個Boolean類型的參數指示這個類是否因IDisposableDispose()方法被調用或者完成器在運行而正在被釋放(完成器與IDisposableDispose()都是委托到該方法上的)如果確定它要被釋放GCSuppressFinalize()就要被調用如果是通過完成器被釋放就要避免再使用你的類中實現完成器的 托管成員因為它們可能已經被終結了
  Figure 提供了一些指導性的說明幫助你在合適的時候在你的類中實現這些結構

問題: 我希望把一些關系到應用程序性能的操作建立在計算機可用內存的基礎上如何才能最簡單地從操作系統獲取這些信息?

答案: 盡管我知道獲取這類信息其他的一些方法但當我發現WMI (Windows Management InstrumentationWindows管理規范)時我發現它才是完成這類工作最佳的方式Win_OperatingSystem類提供了關於操作系統事件的豐富信息SystemManagement命名空間則提供了大量的類來訪問WMI的數據你可以使用 Figure 中的ManagementObjectSearcher類來查詢Win_OperatingSystemTotalVisibleMemorySize的值因為 ManagementObjectCollection (由ManagementObjectSearcher返回)沒有公開訪問集合內部元素的方法因此我使用了一個foreach循環來枚舉出其中的每一個成員而且因為我只關心其中的一個值所以我在第一次枚舉完成後就停止了循環
   注意TotalVisibleMemorySize返回的值可能並不是當前的物理內存總量而是向操作系統報告可利用的內存量你可以從Win_OperatingSystem(Win_OperatingSystem)這個WMI類中學會更多有用的東西

問題: 我嘗試著在未將程序集裝載入我的AppDomain的情況下獲取該程序集的完全限定名這可能做到嗎?

答案: 絕對能!SystemReflectionAssemblyName類有一個static類型的方法GetAssemblyName()這可以返回磁盤上一個程序集的名稱AssemblyName這個方法只是簡單地打開這個程序集文件而不會將它裝載入AppDomain下面的這段代碼就能在控制台上輸出從命令行傳入的路徑參數對應程序集的完全限定名

  static void Main(string [] args)
{
if (argsLength > )
{
try
{
AssemblyName a = AssemblyNameGetAssemblyName(args[]);
ConsoleWriteLine(aFullname);
}
catch(Exception exc)
{
ConsoleWriteLine(excMessage);
}
}
}

注意同樣的技巧也可以用到本地托管的DLL或者EXE上你可以在這些文件上挨個地試一試如果沒有異常被拋出並且返回了一個有效的名字那麼這個文件就是托管的當然這種方式在所有的本地文件都觸發異常的情況下也存在一些性能上的缺陷當然還有另一種方法它不依賴於反射也不需要裝載Portable Executable (PE)而是通過分析DLL或者EXE的PE頭中某個標識位是否被置位由此確定它是否是 托管的Managed Extensions for C++ requently Asked Questions中有實現這種方法的C++代碼等價的C#代碼請參見 Figure

問題: 在我的C#應用程序裡有一大堆的foreach循環當我檢查編譯器生成的MSIL 文件時(Microsoft intermediate languageMicrosoft中間語言)時我發現其中有的循環被嵌入了一個try/finally塊但我的源代碼裡並沒有使用啊?它們為什麼會在這裡出現?

答案: 問得好!記住foreach循環是用來枚舉那些集合型的數據的它是通過獲取一個枚舉器後使用枚舉器的MoveNext操作來實現遍歷並在Current數據屬性中返回集合中的當前項這個枚舉器本身則是通過調用該集合對象的GetEnumerator()方法獲得的因為枚舉器可能實現了IDisposable接口所以C#的編譯器需要在枚舉完成後將其釋放為此編譯器會將這個循環放入一個try塊然後試著在finally塊中釋放這個枚舉器如果利用GetEnumerator ()方法返回的枚舉器實現了IDisposable接口編譯器就會生成一個類似下面這樣的finally塊

  ((IDisposable)enumerator)Dispose();

如果枚舉器沒有實現IDisposable接口那麼編譯器仍然不得不試著釋放這個對象因為不能確定枚舉器的派生類型(從這個函數返回的)沒有實現IDisposable接口於是編譯器又會生成類似這樣的一個finally塊

  IDisposable disposable = enumerator as IDisposable;
if (disposable != null) disposableDispose();

  因為大多數的可枚舉類都有一個GetEnumerator()方法以返回IEnumerator接口(沒有實現IDisposable接口)所以上述的代碼只是編譯器生成結果的一般情形在少數情況下當返回的類型沒有實現IDisposable接口而且是密封的(指不能從其再派生新的類)時編譯器就不需要實現一個try/finally塊了——因為沒有什麼需要釋放的了

問題: 我希望能枚舉出一個對象層中的所有對象但是我不知道如何才能避免很有可能產生的循環引用NET Framework對此提供了什麼幫助沒有?

答案: 遍歷一個對象層中的所有對象並以某種形式將它們序列化非常類似於完成一個BinaryFormatter那樣的遠程格式化程序對此NET Framework也提供了一些很有用的類來完成這類工作SystemRuntimeSerialization命名空間中的ObjectIDGenerator類可以看作是一張專門化的表用於跟蹤對象並賦與每個對象一個唯一的標識符你可以通過這個類查詢對象的ID——它既可以向你提供繼存對象的ID也可以為一個未出現的對象生成一個新的ID正因它能防止你兩次遍歷同一個對象所以你可以很容易地避免循環引用了


From:http://tw.wingwit.com/Article/program/net/201311/12351.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.