C# version of C++ shared_ptr I need something akin to C++'s shared_ptr. Essentially I need an IDisposable item that I can ensure is cleaned up as soon as possible once all references have been removed, but this resource will be shared between many classes.

I don't allow passing in of a an already existing object as it's an easy way to know that the item I'm using isn't being hasn't already been disposed.

Unfortunately I can't think of a way to prevent the user of Item from disposing the underlying item, but as this is to go into my own program I'm not too concerned about that because it intuitively makes sense to not dispose something within a wrapper, provided every class that needs the item uses the wrapper and not just the contained item.

I simply want to know if anyone can see any potential issues with this solution besides what's mentioned.

public class SharedDisposable&lt;T&gt; : IDisposable where T : class, IDisposable {
    static SharedDisposable(){
        instances = new Dictionary&lt;T, List&lt;SharedDisposable&lt;T&gt;&gt;&gt;();
    }

    public SharedDisposable(SharedDisposable&lt;T&gt; share) {
        Item = share.Item;
        instances[Item].Add(this);
    }
    public SharedDisposable(object[] ctorArgs = null) {
        if(ctorArgs == null)
            ctorArgs = new object[] { };

        var ctor = typeof(SharedDisposable&lt;T&gt;).GetConstructor(ctorArgs.Select(a =&gt; a.GetType()).ToArray());
        Item = (T)ctor.Invoke(ctorArgs);
        instances[Item] = new List&lt;SharedDisposable&lt;T&gt;&gt;() { this };
    }
    public SharedDisposable(KeyValuePair&lt;Type, object&gt;[] ctorArgs) {
        var ctor = typeof(SharedDisposable&lt;T&gt;).GetConstructor(ctorArgs.Select(a =&gt; a.Key).ToArray());
        Item = (T)ctor.Invoke(ctorArgs.Select(a =&gt; a.Value).ToArray());
        instances[Item] = new List&lt;SharedDisposable&lt;T&gt;&gt;() { this };
    }

    public void Dispose() {
        var instanceList = instances[Item];
        instanceList.Remove(this);
        if(instances.Count == 0) {
            instances.Remove(Item);
            Item.Dispose();
        }
    }

    public T Item { get; }
    static Dictionary&lt;T, List&lt;SharedDisposable&lt;T&gt;&gt;&gt; instances;
}


Example usage:

class MainForm : Form {
    public MainForm(){
        InitializeComponent();

        Port = new SharedDisposable&lt;SerialPort&gt;("COM5", 9600);
    }
    public MainForm(SharedDisposable&lt;SerialPort&gt; share){
        InitializeComponent();

        if(share == null)
            throw new ArgumentNullException(nameof(share))
        Port = new SharedDisposable&lt;SerialPort&gt;(share);
    }

    public void BtnDevice1Info_Click(object sender, EventArgs e){
        new Device1InfoForm(Port).Show();
    }
    public void BtnDevice2Info_Click(object sender, EventArgs e){
        new Device2InfoForm(Port).Show();
    }

    override Dispose(bool disposing){
        if(disposing){
            Port.Dispose();
        }
        base.Dispose(disposing);
    }

    private SharedDisposable&lt;SerialPort&gt; Port { get; }
}


In my application I actually have a Modbus interface instead of serial port but I just realised the way the constructor is setup won't allow me to use the Modbus API because the Modbus API uses factories instead of constructors, so I'll need to adjust it for that.
But that API should be able to queue messages to be sent out one at a time, preventing any conflicts on the serial port while also allowing multiple forms or even applications to use the same serial port. In order to make this happen I need multiple forms to be able to share a disposable resource, but none of them to clean it up unless they're the last holder of that resource.
public SharedDisposable(object[] ctorArgs = null)
{
  if (ctorArgs == null)
      ctorArgs = new object[] { };

  var ctor = typeof(SharedDisposable&lt;T&gt;).GetConstructor(ctorArgs.Select(a =&gt; a.GetType()).ToArray());
  Item = (T)ctor.Invoke(ctorArgs);
  instances[Item] = new List&lt;SharedDisposable&lt;T&gt;&gt;() { this };
}



This constructor is not a very good idea because the use needs to exactly know the signature of the constructor in order to create T.

A much better way would be to let him specify a factory lambda. Then all it will be left is:

public SharedDisposable(Func&lt;T&gt; create)
{
    Item = create();
    instances[Item] = new List&lt;SharedDisposable&lt;T&gt;&gt;() { this };
}


so now he can call it in a much easier fashion like:

Port = new SharedDisposable&lt;SerialPort&gt;(() =&gt; new SerialPort("COM5", 9600));


The other constructor using KeyValuePairs is now obsolete.




public SharedDisposable(object[] ctorArgs = null)
{
  if (ctorArgs == null)
      ctorArgs = new object[] { };
  ...
}



Btw. you can make this look cleaner with the coalesce operator: ??

ctorArgs = ctorArgs ?? new object[] { };




Objects like this one usully use the name Value for what you chose Item.



Its usage might be easier in certain scenarios if you added an implicit cast operator to T:

public static implicit operator T(SharedDisposable&lt;T&gt; share) =&gt; share.Value;




If you replaced the List by a HashSet the Dispose method could be simplified to:

public void Dispose()
{
    if (instances[Value].Remove(this) &amp;&amp; instances[Value].Count == 0)
    {
        instances.Remove(Value);
        Value.Dispose();
    }
}