I'm trying to find a way to create an object which is roughly the reverse of the Delegate type.
Specifically, an object which contains a reference to the delegate instance, the instance of the class hosting the delegate instance, and the delegate instance's type.
The objective is to enable a sea of publishing objects and subscribing objects which can be properly Dispose()
ed.
As in, the subscribers can be disposed without directly calling Publisher?.DoPublish -= DoAThing;
, and the publishers can empty delegate instances of invokable members without resulting in the subscriber retaining a reference to the publisher in the collection it uses to keep track of its own subscriptions.
As a basic mechanical problem, consider the following:
using System; //Delegate
public delegate void Publish();
public class Publisher
{
public Publish DoPublish;
}
public class Subscriber
{
public void DoAThing()
{
Console.WriteLine("I Did a Thing.");
}
}
public class DelegateProxy<TDelegate> where TDelegate : Delegate
{
public TDelegate DoDelegate;
public DelegateProxy(ref TDelegate publisherDelegateInstance)
{
//This appears to be treated like a value assignment, not a reference assignment
//At this point, the DelegateProxy.DoDelegate and publisherDelegateInstance behavior diverges.
DoDelegate = publisherDelegateInstance;
}
public void SubscribeDelegate(TDelegate subscriberCallableEntity)
{
//DoDelegate += subscriberCallableEntity; //CS0019: Operator += cannot be applied to operands of type 'TDelegate' and 'TDelegate'
DoDelegate = (TDelegate)Delegate.Combine(DoDelegate, subscriberCallableEntity); //requires explicit cast to be accepted by compiler
}
}
static void Main(string[] args)
{
Publisher myPublisher = new Publisher();
Subscriber mySubscriber = new Subscriber();
DelegateProxy<Publish> myProxy = new DelegateProxy<Publish>(ref myPublisher.DoPublish);
myProxy.SubscribeDelegate(mySubscriber.DoAThing);
myPublisher.DoPublish?.Invoke(); //No output
Console.ReadLine();
myProxy.DoDelegate?.Invoke(); //output occurs here.
Console.ReadLine();
}
As coded in the example, there is no output when the original publisher's delegate is invoked., as the delegate which gets Delegate.Combine()
ed appears to be a copy rather than a reference assignment. The ref
keyword also appears to be superfluous, code behaves the same with or without it.
Is there a way to make a generic class as illustrated (DelegateProxy
) and use said class to perform a subscription on the publisher, on behalf of the subscriber, without statically referencing the instance (myPublisher) which owns the delegate instance?
In the actual use case, it is necessary for the subscriber to be able to unsubscribe to an arbitrary publisher, as both the publishers and subscribers are ephemeral within the greater hierarchy of the software library. So not having a reference of some sort to which publisher(s) the subscriber is subscribed to is not possible. Both the publisher and the subscriber need to be able to Dispose()
ed in any order without negative impact (of either calling a subscriber's delegate when the subscriber is Dispose()
ed or retaining a reference to a Dispose()
ed publisher.