The garbage Collection in the. Net Framework helps programmers automatically reclaim managed resources, which is a comfortable experience for callers of class libraries: Any object can be created at any point, at any time, and the GC will always end up backstop. His shoes, what do you need to do to provide such a good experience when you are a library provider?
First, what are the managed resources within the. Net framework and which are unmanaged resources?
Basically, all classes within the. Net framework are managed resources, including a variety of streams (such as FileStream, MemoryStream), database connection, components, and so on.
Can write a simple small program verification: (Take FileStream as an example)
A method to monitor whether a file is being occupied in a background thread:
private static void Monitorfilestatus (String fileName) {Console.WriteLine ("Start to monitor fil E: {0} ", fileName); Task.Factory.StartNew (() = {while (true) {bool Isinuse = Isfileinuse (FileName); String Messageformat = Isinuse? ' File {0} is ' in use. ': ' file {0} is released. '; Console.WriteLine (Messageformat, FileName); Thread.Sleep (Oneseconds); } }); } private static bool Isfileinuse (string fileName) {bool Isinuse = true; FileStream stream = null; try {stream = File.Open (FileName, Filemode.append, FileAccess.Write); Isinuse = false; } catch {} finally {if (stream! = null) {stream. Dispose (); }} return isinuse; }
To write a method that takes up a file, FileStream is just a local variable, and it should be recycled when the method returns:
private static void OpenFile () { FileStream stream = File.Open (Testfilename, Filemode.append, FileAccess.Write); Wait (fiveseconds); }
The last is an essential waiting:
private static void Wait (TimeSpan time) { Console.WriteLine ("Wait for {0} seconds ...", time. totalseconds); Thread.Sleep (time); }
Merging together is a test:
Start the file monitoring thread first, and then open the file without.
OpenFile method returns, predicting FileStream is recycled
Then call the GC to see if the file is released.
private static void Filetest () { monitorfilestatus (testfilename); OpenFile (); CALLGC (); Wait (fiveseconds); }
The result of the operation shows that the GC automatically recycles the FileStream automatically. There is no need to call the Dispose method or use the using
So, what are the unmanaged resources?
Typically, the PInvoke of Windows APIs, various IntPtr are unmanaged resources. For example, the same open file, if written as follows, includes an unmanaged resource
[Flags] Internal enum Openfilestyle:uint {of_cancel = 0x00000800,//ignored. For a dialog box with a Cancel button, use Of_prompt. Of_create = 0x00001000,//creates a new file. If file exists, it is truncated to zero (0) length. Of_delete = 0x00000200,//Deletes a file. Of_exist = 0x00004000,//Opens a file and then closes it. Used to test this a file exists Of_parse = 0x00000100,//Fills the OFSTRUCT structure, but does don't do anything E Lse. Of_prompt = 0x00002000,//displays a dialog box if a requested file does not exist Of_read = 0x00000000,//Ope NS a file for reading only. Of_readwrite = 0x00000002,//Opens a file with Read/write permissions. Of_reopen = 0x00008000,//Opens a file by using information in the REOPEN buffer. For ms-dos–based file systems, opens a file with compatibility mode, allows any process on a//specified Compu ter to open the file any number of TimEs. Other efforts to open a file with other sharing modes fail. This flag was mapped to the//file_share_read| File_share_write Flags of the CreateFile function. Of_share_compat = 0x00000000,//Opens a file without denying read or write access to other processes. On ms-dos-based file systems, if the file have been opened in compatibility mode/by any other process, the fun Ction fails. This flag was mapped to the file_share_read| File_share_write Flags of the CreateFile function. Of_share_deny_none = 0x00000040,//Opens a file and denies read access to other processes. On ms-dos-based file systems, if the file have been opened in compatibility mode,//or for read access by any OT Her process, the function fails. This flag was mapped to the FILE_SHARE_WRITE flag of the CreateFile function. Of_share_deny_read = 0x00000030,//Opens a file and denies write access to other processEs. On ms-dos-based file systems, if a file have been opened in compatibility mode,//or for write access by any oth Er process, the function fails. This flag was mapped to the FILE_SHARE_READ flag of the CreateFile function. Of_share_deny_write = 0x00000020,//Opens a file with exclusive mode, and denies both Read/write access to other P Rocesses. If a file has been opened in any other mode for read/write access, even by the current process,//the function Fails. Of_share_exclusive = 0x00000010,//verifies that the date and time of a file is the same as when it is opened PR eviously. This is useful as a extra check for read-only files. Of_verify = 0x00000400,//Opens a file for write access is only. Of_write = 0x00000001} [StructLayout (layoutkind.sequential)] internal struct ofstruct {public byte CB Ytes; public byte Ffixeddisc; Public UInt16 NerrcodE Public UInt16 Reserved1; Public UInt16 Reserved2; [MarshalAs (UnmanagedType.ByValTStr, SizeConst = +)] public string szpathname; } class WindowsAPI {[DllImport ("kernel32.dll", bestfitmapping = False, Throwonunmappablechar = True)] Internal static extern IntPtr OpenFile ([MarshalAs (UNMANAGEDTYPE.LPSTR)]string lpFileName, out Ofstruct Lpreopenbuff, Openfilestyle Ustyle); [DllImport ("kernel32.dll", SetLastError = True)] [ReliabilityContract (Consistency.willnotcorruptstate, cer.success)] [SuppressUnmanagedCodeSecurity] [Return:marshalas (Unmanagedtype.bool)] internal static extern Bool CloseHandle (IntPtr hobject); }
To handle unmanaged resources, you need to implement IDisposable interface. There are two reasons:
Destructors cannot be relied upon because the invocation of a heterogeneous function is determined by the GC. Unable to release scarce resources in real time.
There is a general principle of processing: destructors handle managed resources, and IDisposable interface handle managed and unmanaged resources.
As the above example, the implementation code is completed as follows:
public class Unmanagedfileholder:ifileholder, IDisposable {private IntPtr _handle; private string _filename; Public Unmanagedfileholder (String fileName) {_filename = FileName; } public void OpenFile () {Console.WriteLine ("Open file with Windows API."); Ofstruct info; _handle = Windowsapi.openfile (_filename, out info, openfilestyle.of_readwrite); } #region IDisposable Support private bool disposed = false; protected virtual void Dispose (bool disposing) {if (!disposed) {if (disposin g) {//No managed resource} windowsapi.closehandle (_hand Le); _handle = IntPtr.Zero; disposed = true; }} ~unmanagedfileholder () {Dispose (false); } public void Dispose () { Dispose (TRUE); Gc. SuppressFinalize (this); } #endregion}
What if there are both managed resources and unmanaged resources in the same class?
You can follow the following pattern:
Class Hybridpattern:idisposable { private bool _disposed = false; ~hybridpattern () { Dispose (false); } protected void Dispose (bool disposing) { if (_disposed) { return; } if (disposing) { //Code to dispose the managed resources of the class //Internalcomponent1.dispose ();
} //Code to dispose the un-managed resources of the class //CloseHandle (handle); handle = IntPtr.Zero; _disposed = true; } public void Dispose () { Dispose (true); Gc. SuppressFinalize (this); } }
The following is a complete example of a managed FileStream, as well as an unmanaged Handler
public class Hybridholder:ifileholder, IDisposable {private string _unmanagedfile; private string _managedfile; Private INTPTR _handle; Private FileStream _stream; Public Hybridholder (String unmanagedfile, String managedfile) {_unmanagedfile = Unmanagedfile; _managedfile = Managedfile; } public void OpenFile () {Console.WriteLine ("Open file with Windows API."); Ofstruct info; _handle = Windowsapi.openfile (_unmanagedfile, out info, openfilestyle.of_readwrite); Console.WriteLine ("Open file with. Net libray."); _stream = File.Open (_managedfile, Filemode.append, FileAccess.Write); } #region IDisposable Support private bool disposed = false; protected virtual void Dispose (bool disposing) {if (!disposed) {//CONSOLE.WR Iteline ("string is null?") {0} ", _stream = = null); if (disposing && _stream! = null) {Console.WriteLine ("Clean up Manage D resource. "); _stream. Dispose (); } Console.WriteLine ("Clean Up Unmanaged resource."); Windowsapi.closehandle (_handle); _handle = IntPtr.Zero; disposed = true; }} ~hybridholder () {Dispose (false); public void Dispose () {Dispose (true); Gc. SuppressFinalize (this); } #endregion}
Finally, what if there is no class that implements IDisposable interface? For example byte[], StringBuilder
Do not interfere with their recycling at all, the GC is doing well.
Tried to set a large byte[] to null in a destructor, and the only result is that its collection is deferred to the next GC cycle.
The reason is also simple, each time a reference is brought to the count on its reference tree plus one.
Full code See Github:
Https://github.com/IGabriel/IDisposableSample