Some GC thoughts on making sure objects are cleaned up.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Runtime.CompilerServices; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace GC | |
{ | |
/* | |
In dotnet core, finalizer does not appear to be called on shutdown | |
*/ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
DoThing(); | |
System.GC.Collect(); | |
System.GC.WaitForPendingFinalizers(); | |
DoThing(); | |
} | |
static void DoThing() | |
{ | |
var thing1 = new Thing(); | |
} | |
} | |
public class Thing : Cleanup | |
{ | |
protected override void DisposeManaged() | |
{ | |
WriteLine(); | |
} | |
} | |
public class Thing2 : ICleanup | |
{ | |
~Thing2() | |
{ | |
ICleanup.Dispose2(this, false); | |
System.GC.SuppressFinalize(this); | |
} | |
public Thing2() | |
{ | |
this.RegisterForCleanup(); | |
} | |
bool ICleanup.IsDisposed { get; set; } | |
void ICleanup.DisposeManaged() | |
{ | |
} | |
void ICleanup.DisposeUnmanged() | |
{ | |
} | |
} | |
public interface ICleanup : IDisposable | |
{ | |
void IDisposable.Dispose() | |
{ | |
Dispose(true); | |
} | |
bool IsDisposed { get; protected set; } | |
protected static void Dispose2(ICleanup thing, bool disposing) | |
{ | |
thing.Dispose(disposing); | |
} | |
protected void Dispose(bool disposing) | |
{ | |
if (IsDisposed) return; | |
if (disposing) | |
{ | |
DisposeManaged(); | |
} | |
DisposeUnmanged(); | |
IsDisposed = true; | |
} | |
protected void DisposeManaged(); | |
protected void DisposeUnmanged(); | |
} | |
public abstract class Cleanup : ICleanup | |
{ | |
private static int _instanceCount; | |
private int _id = Interlocked.Increment(ref _instanceCount); | |
~Cleanup() | |
{ | |
WriteLine(); | |
ICleanup.Dispose2(this, false); | |
System.GC.SuppressFinalize(this); | |
} | |
bool ICleanup.IsDisposed { get; set; } | |
protected Cleanup() | |
{ | |
WriteLine(); | |
this.RegisterForCleanup(); | |
} | |
protected abstract void DisposeManaged(); | |
protected virtual void DisposeUnmanged() { } | |
protected void WriteLine(string message = null, [CallerMemberName] string name = null) | |
=> Console.WriteLine($"{name} {_id} {message ?? "was called"}"); | |
void ICleanup.DisposeManaged() => DisposeManaged(); | |
void ICleanup.DisposeUnmanged() => DisposeUnmanged(); | |
} | |
public static class Disposable | |
{ | |
public static void RegisterForCleanup(this IDisposable disposable) | |
{ | |
var myself = new WeakReference<IDisposable>(disposable); | |
AppDomain.CurrentDomain.ProcessExit | |
+= (_, __) => | |
{ | |
if (myself.TryGetTarget(out IDisposable thing2)) | |
{ | |
thing2.Dispose(); | |
} | |
else | |
{ | |
Console.WriteLine("WeakRef dead"); | |
} | |
}; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment