Can a delegate instance be referenced in a useable way? [duplicate] - c#

This question already has answers here:
Setting a ref to a member field in C#
(6 answers)
Closed 4 months ago.
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.

Delegate combining has to happen on the callee side. There's no going around this, not with ref parameters, not with anything. I believe you discovered this the hard way, but you could also have just read the documentation on delegates.
With that in mind, a quick change to your code to get it working is as follows:
delegate void Publish();
class Publisher
{
public Publish DoPublish;
public void SubscribePublishHandler(Publish handler) =>
DoPublish += handler;
}
class Subscriber
{
public void DoAThing()
{
Console.WriteLine("I Did a Thing.");
}
}
class DelegateProxy<TDelegate> where TDelegate : Delegate
{
readonly Action<TDelegate> subscribeToHandlerAction;
public DelegateProxy(Action<TDelegate> subscribeToHandlerAction) =>
this.subscribeToHandlerAction = subscribeToHandlerAction;
public void SubscribeDelegate(TDelegate subscriberCallableEntity) =>
subscribeToHandlerAction(subscriberCallableEntity);
}
static class Program
{
static void Main()
{
Publisher myPublisher = new();
Subscriber mySubscriber = new();
DelegateProxy<Publish> myProxy = new(myPublisher.SubscribePublishHandler);
myProxy.SubscribeDelegate(mySubscriber.DoAThing);
myPublisher.DoPublish?.Invoke(); // Will output
}
}
This will output your string when calling the publisher delegate as expected.
I will however reiterate that you're reinventing the wheel here, and not any better than the alternatives. That makes sense, given that your requirements also don't seem to make any sense ("So not having a reference of some sort to which publisher(s) the subscriber is subscribed to is not possible"). There are much better implementations of logging frameworks available that push you towards sane practices by just using them.

Related

Allow factory executed code to raise events when that code is a `Func<TIn, TOut>`?

I'm building a retry system that allows me to attempt code multiple times before giving up (useful for things like establishing connections over the network). With this, the basic code I usually copy and paste everywhere as a base is:
for (int i = 0; i < attemptThreshold; i++) {
try {
...
break;
} catch (Exception ex) { ... }
}
There's quite a bit of logging code within the try and catch blocks that can be delegated by refactoring to ensure consistency. It's straightforward to refactor it and delegate the work of retrying:
public static class DelegateFactory {
public static bool DelegateWork<TIn, TOut>(Func<TIn, TOut> work, int attemptThreshold, TIn input, out TOut output) {
if (work == null)
throw new ArgumentException(...);
for (int i = 0; i < attemptThreshold; i++) {
try {
OnMessageReceived?.Invoke(work, new FactoryEventArgs("Some message..."));
output = work(input);
return true;
} catch (Exception e) { OnExceptionEncountered?.Invoke(work, new FactoryEventArgs(e)); }
}
return false;
}
public static event EventHandler<FactoryEventArgs> OnMessageReceived;
public static event EventHandler<FactoryEventArgs> OnExceptionEncountered;
}
Calling it is also straightforward:
DelegateFactory.DelegateWork((connectionString) => {
using (SqlConnection conn = new SqlConnection(connectionString))
conn.Open();
}, 10, "ABC123", out bool connectionMade);
Console.WriteLine($"Connection Made: {(connectionMade ? "Yes" : "No")}");
Keep in mind, the above code excludes the definition for FactoryEventArgs, but it's just a class that takes an object as an argument (for simplicity in prototyping). Now, what I have above works just fine, but I wanted to add a way to allow the caller to post messages using an event for the factory's subscriber to log (the whole single responsibility thing, which I'm still learning, by the way, so be gentle). The idea is to create an event called OnMessageReceived and a public method called PostMessage that can only be called from code being executed by the factory. If the call is made from any other place then it would throw an InvalidOperationException to signify that the call was invalid. My first though to accomplish this is to use the call stack to my advantage:
using System.Diagnostics; // Needed for StackFrame
...
public static void PostMessage(string message) {
bool invalidCaller = true;
try {
Type callingType = new StackFrame(1).GetType();
if (callingType == typeof(DelegateFactory))
invalidCaller = false;
} catch { /* Gracefully ignore. */ }
if (invalidCaller)
throw new InvalidOperationException(...);
OnMessageReceived?.Invoke(null, new FactoryEventArgs(message));
}
However, I don't know for sure that this would prove to be reliable. The idea though is to allow the work to also send messages to the subscriber, but that may be a moot point because the object containing the work could just raise it's own OnMessageReceived event instead. I just don't like the idea of the exceptions being sent to the subscriber one way, and messages going out another. Maybe I'm just being picky? Starting to have a smell, the more I think on it.
EXAMPLE USE CASE
public class SomeObjectUsingTheFactory {
public bool TestConnection() {
DelegateFactory.DelegateWork((connectionString) => {
// Completely valid.
DelegateFactory.PostMessage("Attempting to establish a connection to SQL server.");
using (SqlConnection conn = new SqlConnection(connectionString))
conn.Open();
}, 3, "ABC123", out bool connectionMade);
// This should always throw an exception.
// DelegateFactory.PostMessage("This is a test.");
return connectionMade;
}
}
public class Program {
public static void Main(string[] args) {
DelegateFactory.OnMessageReceived += OnFactoryMessageReceived;
var objNeedingFactory = new SomeObjectUsingTheFactory();
if (objNeedingFactory.TestConnection())
Console.WriteLine("Connected.");
}
public static void OnFactoryMessageReceived(object sender, FactoryEventArgs e) {
Console.WriteLine(e.Data);
}
public static void OnFactoryExceptionOccurred(object sender, FactoryEventArgs e) {
string errorMessage = (e.Data as Exception).Message;
Console.WriteLine($"An error occurred. {errorMessage}");
}
}
In the example above, if we assume the connection continues to fail, the output should be:
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
If it succeeds on the second attempt it should be:
Attempting to establish a connection to SQL server.
An error occurred. {errorMessage}
Attempting to establish a connection to SQL server.
Connected.
How can I ensure the method PostMessage is only called by code being executed by the factory?
NOTE: I'm not against changing the design if it's introducing bad practice. I'm completely open to new ideas.
COMPILER ERRORS: Also, any compilation errors in here are strictly oversights and typos. I manually typed this question up as I tried my best to work my way through the problem. If you encounter any issues, let me know and I'll fix them promptly.
You can do away with the stack-based security by introducing a context object that provides access to the event.
But first, a few notes. I'm not going to speak to the merits of this design because that's subjective. However, I will address some terminology, naming, and design matters.
.NET's naming convention for events does not includethe "On" prefix. Rather, the method that raises the event (marked private or protected virtual, depending on whether you can inherit the class) has the "On" prefix. I've followed this convention in the code below.
The name "DelegateFactory" sounds like something that create delegates. This does not. It accepts a delegate and you're using it to perform an action within a retry loop. I'm having a tough time word-smithing this one, though; I've called the class Retryer and the method Execute in the code below. Do with that what you will.
DelegateWork/Execute return a bool but you never check it. It's unclear if that's an oversight in the example consumer code or a flaw in this thing's design. I'll leave it to you to decide, but because it follows the Try pattern to determine if the output parameter is valid, I'm leaving it there and using it.
Because you're talking about network-related actions, consider writing one or more overloads that accept an awaitable delegate (i.e. returns Task<TOut>). Because you can't use ref or out parameters with async methods, you'll need to wrap the bool status value and the return value of the delegate in something, such as a custom class or a tuple. I will leave this as an exercise to the reader.
If an argument is null, make sure you throw ArgumentNullException and simply pass it the name of the argument (e.g. nameof(work)). Your code throws ArgumentException, which is less specific. Also, use the is keyword to ensure you're doing a reference equality test for null and not accidentally invoking overloaded equality operators. You'll see that in the code below, as well.
Introducing a Context Object
I'm going to use a partial class so that the context is clear in each snippet.
First, you have the events. Let's follow the .NET naming convention here because we want to introduce invoker methods. It's a static class (abstract and sealed) so those will be private. The reason for using invoker methods as a pattern is to make raising an event consistent. When a class can be inherited and an invoker method needs to be overridden, it has to call the base implemention to raise the event because the deriving class doesn't have access to the event's backing storage (that could be a field, as in this case, or perhaps the Events property in a Component-derived type where the key used on that collection is kept private). Although this class is uninheritable, it's nice to have a pattern you can stick to.
The concept of raising the event is going to go through a layer of semantic translation, since the code that registers the event handler may not be the same as the code that calls this method, and they could have different perspectives. The caller of this method wants to post a message. The event handler wants to know that a message has been received. Thus, posting a message (PostMessage) gets translated to notifying that a message has been received (OnMessageReceived).
public static partial class Retryer
{
public static event EventHandler<FactoryEventArgs> MessageReceived;
public static event EventHandler<FactoryEventArgs> ExceptionEncountered;
private static void OnMessageReceived(object sender, FactoryEventArgs e)
{
MessageReceived?.Invoke(sender, e);
}
private static void OnExceptionEncountered(object sender, FactoryEventArgs e)
{
ExceptionEncountered?.Invoke(sender, e);
}
}
Side note: You might want to consider defining a different EventArgs-derived class for ExceptionEncountered so you can pass the whole exception object for that event rather than whatever string data you cobble together from it.
Now, we need a context class. What will be exposed to the consumer is either an interface or an abstract base class. I've gone with an interface.
The semantic translation from "post a message" to "a message was received" is aided by the fact that FactoryEventArgs is unknown to the lambda that's posting the message. All it has to do is pass the message as a string.
public interface IRetryerContext
{
void PostMessage(string message);
}
static partial class Retryer
{
private sealed class RetryerContext : IRetryerContext
{
public void PostMessage(string message)
{
OnMessageReceived(this, new FactoryEventArgs(message));
}
}
}
The RetryerContext class is nested in the Retryer class (and private) for two reasons:
It needs access to at least one of the invoker methods that's private to the Retryer class.
Given the first point, it simplifies things by not exposing a nested class to the consumer.
Generally speaking, nested classes should be avoided, but this is one of those things that they're exactly designed to do.
Also note that the sender is this, i.e. the context object. The original implementation was passing work as the sender, which is not what's raising (sending) the event. Because it's a static method in a static class, there was no instance to pass before and passing null probably felt dirty; strictly speaking, the context is still not what's raising the event, but it's a better candidate than a delegate instance. It will also be passed as the sender when being used inside of Execute.
The implementation needs to be modified just slightly to include the context when invoking work. The work argument is now a Func<TIn, IRetryerContext, TOut>.
static partial class Retryer
{
public static bool Execute<TIn, TOut>(Func<TIn, IRetryerContext, TOut> work, int attemptThreshold, TIn input, out TOut output)
{
if (work is null)
throw new ArgumentNullException(nameof(work));
DelegationContext context = new DelegationContext();
for (int i = 0; i < attemptThreshold; i++)
{
try
{
OnMessageReceived(context, new FactoryEventArgs("Some message..."));
output = work(input, context);
return true;
}
catch (Exception e)
{
OnExceptionEncountered(context, new FactoryEventArgs(e.Message));
}
}
output = default;
return false;
}
}
OnMessageReceived is called from two different places: Execute and PostMessage, so if you ever need to change how the event is raised (maybe some add logging), it only needs to be changed in one place.
At this point, the problem of preventing unwanted message posting is solved because:
The event can't be raised arbitrarily since anything that calls it is private to the class.
A message can only be posted by something that has been given the ability to do so.
Small nit-pick: Yes, the caller could capture a local variable and assign the context to the outer scope, but then someone could also use reflection to find the event delegate's backing field and invoke it whenever they want, too. There's only so much you can reasonably do.
Finally, the consumer code needs to include the context in the lambda's parameters.
Here's your example use case, modified to use the implementation above. The lambda returns a string, the connection's current database, as the result of the operation. That's distinct and separate from the true/false returned to indicate whether it was a success after attemptThreshold attempts, which is now what's assigned to connectionMade.
public class SomeObjectUsingTheFactory
{
public bool TestConnection(out string currentDatabase)
{
bool connectionMade = Retryer.Execute((connectionString, context) =>
{
// Completely valid.
context.PostMessage("Attempting to establish a connection to SQL server.");
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
return conn.Database;
}
}, 3, "ABC123", out currentDatabase);
// Can't call context.PostMessage here because 'context' doesn't exist.
return connectionMade;
}
}
public class Program
{
public static void Main(string[] args)
{
Retryer.MessageReceived += OnFactoryMessageReceived;
var objNeedingFactory = new SomeObjectUsingTheFactory();
if (objNeedingFactory.TestConnection(out string currentDatabase))
Console.WriteLine($"Connected to '{currentDatabase}'.");
}
public static void OnFactoryMessageReceived(object sender, FactoryEventArgs e)
{
Console.WriteLine(e.Data);
}
public static void OnFactoryExceptionOccurred(object sender, FactoryEventArgs e)
{
string errorMessage = (e.Data as Exception).Message;
Console.WriteLine($"An error occurred. {errorMessage}");
}
}
As further exercises, you could also implement other overloads. Here are some examples:
An overload for a lambda that doesn't need to call PostMessage and therefore doesn't need the context. This has the same type for the dowork parameter as your original implementation.
public static bool Execute<TIn, TOut>(Func<TIn, TOut> work, int attemptThreshold, TIn input, TOut output)
{
return Execute((arg, _ /*discard the context*/) => work(arg), attemptThreshold, input, out output);
}
Overloads for lambdas that don't need need to return a value in an output parameter, and therefore use Action delegates instead of Func delegates.
public static bool Execute<TIn>(Action<TIn, IRetryerContext> work, int attemptThreshold, TIn input)
{
// A similar implementation to what's shown above,
// but without having to assign an output parameter.
}
public static bool Execute<TIn>(Action<TIn> work, int attemptThreshold, TIn input)
{
return Execute((arg, _ /*discard the context*/) => work(arg), attemptThreshold, input);
}

C# method resolution surprise when using a "generic handler builder"

Today I stumbled upon a problem, which I couldn't explain to myself. I was trying to build a "generic handler builder", to clean up repetitive code in my project. I thought this could be easily achieved with some generics and the power of Action<T>.
I came up with a similar solution like this (this is a simplified version to show the "problem"):
using System;
namespace GenericActionBuilder {
public class FirstMessage { }
public class MessageHandler {
public void HandleMessage(object msg)
=> Console.WriteLine("Fallback object handler");
public void HandleMessage(FirstMessage msg)
=> Console.WriteLine("FirstMessage handler");
}
class Program
{
public static Action<MessageType> BuildHandler<MessageType>() {
Action<MessageType> handler = (msg) => {
Console.WriteLine($"Called with Type={msg.GetType().Name}");
var messageHandler = new MessageHandler();
messageHandler.HandleMessage(msg);
};
return handler;
}
static void Main(string[] args)
{
// This is the surprise
var generatedHandler = BuildHandler<FirstMessage>();
var msg = new FirstMessage();
Console.WriteLine($"Calling generated handler with type={msg.GetType().Name}");
generatedHandler(msg);
// This gives the expected behavior
var myHandler = new MessageHandler();
myHandler.HandleMessage(msg);
}
}
}
Output:
Calling generated handler with type=FirstMessage
Called with Type=FirstMessage
Fallback object handler
FirstMessage handler
Of course I would expect that, when I call HandleMessage with a FirstMessage object at hand, C# would pick the best matching overload: HandleMessage(FirstMessage). This is what happens if you don't utilize the "generic handler builder".
And even inside the generated handler the type of the msg is still FirstMessage. But still the call handler.HandleMessage(msg) triggers the "fallback" method with the object signature.
Can anybody explain this to me?
C# would pick the best matching overload
Yes, but it will do it on compile time (not runtime) based on information about type, which C# compiler can obtain from code.
Your handler has a generic parameter, but this parameter is not limited by any condition, so, it can be any type: FirstMessage, object or even Program and etc, then knowing this, the compiler will select "the best matching overload", which obviously is HandleMessage(object) because it can accept any provided type. To quickly check it, you can create base class/interface (Message) for messages and limit generic parameter to something like: where MessageType : Message and provide appropriate methods in MessageHandler class and you will see that the compiler will select HandleMessage(Message) instead of HandleMessage(object).
So, you can try to implement visitor (double dispatch) pattern to solve this problem (I assume that you have more message types).

C# When explicit delegate declaration is required when working with events [duplicate]

This question already has answers here:
Creating delegates manually vs using Action/Func delegates
(8 answers)
Closed 6 years ago.
all we know delegate is function pointer. so when we work with event then we declare delegate but i like to know in which kind of situation we have to declare delegate when working with events like public delegate void OnButtonClickDelegate();
here i am refering a code sample which show we can use in built delegate EventHandler instead of declare delegate explicitly.
public class ArgsSpecial : EventArgs
{
public ArgsSpecial (string val)
{
Operation=val;
}
public string Operation {get; set;}
}
public class Animal
{
// Empty delegate. In this way you are sure that value is always != null
// because no one outside of the class can change it.
public event EventHandler<ArgsSpecial> Run = delegate{}
public void RaiseEvent()
{
Run(this, new ArgsSpecial("Run faster"));
}
}
Animale animal= new Animal();
animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
so when EventHandler in built delegate solve my purpose then we no need to declare delegate explicitly when working with events. so tell me where we have to declare delegate explicitly and EventHandler may not solve our purpose. thanks
EventHandler actually has a very limited signature, two parameters where the first is always type object, and the second has to be a class type inheriting System.EventArgs. But considering System.Action<T, ...> and System.Func<T, ..., TReturn>, it is very rare that you need to declare another. In fact most of the original BCL delegate types MethodInvoker, Converter<T, U>, Predicate<T>, and even EventHandler<T> probably wouldn't have been defined if the Action and Func families had existed at that time.
Where you do still need to declare your own delegate is when you need by-reference parameters. The ref and out keyword cannot be passed through a generic type parameter, so if you want a delegate matching the TryParse family, you'd be unable to say Func<string, out T, bool> and would need a custom declaration
/* return true if parsing successful */
delegate bool TryParser<T>(string from, out T into);
It's very uncommon to use by-reference parameters with events, so you'll probably never have to declare a delegate for use as an event type.
You need a custom delegate in cases where you want an event that receives parameters others than those specified by EventHandler
For example, assume your Animal class wants to raise an event each time another Animal tried to eat it, this event might need to send both the predator and the animal being attacked. With EventHandler you'll need to create a new class as a wrapper for both animals and send an instance of it as the event parameter, it is simpler with a delegate:
public delegate void AttackedDelegate(Animal predator, Animal victim);
//...
public event AttackedDelegate OnAttacked;
Although with the introduction of the Action<> delegates I think it is a lot less common that you would need to declare your own for reasons other than readability. So the above example can be expressed as
public event Action<Animal, Animal> OnAttacked;

Compile-time information lost with conversion to object, cannot cast to generic interface<T>

I have a class implementing a concrete typed version of a generic interface. I have found that if I pass an object in to my function (even though it might be the correct object) at compile time it is still considered an object and thus failing at runtime with the error:
Unable to cast object of type 'TestEventHandler' to type 'IDomainEventHandler '1[System.Object]'.
I am deserializing messages from a bus (which products objects, which should have an associated DomainEventHandler<of_deserialized_type> associated with the message.
In summary, the problem I believe is IDomainEventHandler<T> not casting from `IDomainEventHandler<object>, I would appreciate guidance on how best to solve this issue and still maintain the generic IDomainEventHandler<T> interface even with objects being passed in to Publish().
[TestClass]
public class InternalMessageHandlerTests
{
class TestEvent
{
}
class TestEventHandler : IDomainEventHandler<TestEvent>
{
public void HandleEvent(TestEvent domainEvent)
{
}
}
[TestMethod]
public void Test()
{
TestEvent testEvent = new TestEvent();
object testEventAsObject = testEvent; // compile time type information lost
Publish(testEvent); // this is OK :)
Publish(testEventAsObject); // this fails :(
}
public void Publish<T>(T eventToPublish) where T : class
{
var handlerInstance = new TestEventHandler();
IDomainEventHandler<T> converted = (IDomainEventHandler<T>)handlerInstance;
converted.HandleEvent(eventToPublish);
}
}
You can't convert TestEventHandler to IDomainEventHandler<object> since this is not safe. If it were allowed you could do:
IDomainEventHandler<object> converted = (IDomainEventHandler<object>)new TestEventHandler();
converted.HandleEvent("dsfs");
which is invalid since TestEventHandler requires its argument to HandleEvent to be TestEvent.
You could invoke Publish using reflection:
TestEvent testEvent = new TestEvent();
object testEventAsObject = testEvent;
var publishMethod = this.GetType().GetMethod("Publish").MakeGenericMethod(testEventAsObject.GetType());
publishMethod.Invoke(this, new object[] { testEventAsObject });
In this situation, it is not possible to support a fully-generic interface without reflection. And reflection is a pretty bad idea for such a simple case.
My normal approach for these situations is IDomainEventHandler, then DomainEventHandlerBase<T> : IDomainEventHandler so that inheriting classes get all advantage of generics, but outside interface can accept objects.
Obviously it is not statically safe, but as I said it is not possible to be statically safe here. As soon as you assigned your instance to object testEventAsObject, you made it possible for this variable to contain anything from the compiler point of view (string, int, anything).
For the things like service bus where you have to select the right handler, it would look like this:
public interface IDomainEventHandler {
void HandleEvent(object domainEvent);
bool CanHandleEvent(object domainEvent);
}
public abstract class DomainEventHandlerBase<T> : IDomainEventHandler {
public abstract void HandleEvent(T domainEvent);
public abstract bool CanHandleEvent(T domainEvent);
void IDomainEventHandler.HandleEvent(object domainEvent) {
return HandleEvent((T)domainEvent);
}
bool IDomainEventHandler.CanHandleEvent(object domainEvent) {
return (domainEvent is T) && CanHandleEvent((T)domainEvent);
}
}
I has written that directly (without checking in VS) so mistakes are possible.
After that when you receive a message just select
handlers.First(h => h.CanHandleEvent(domainEvent))

How can a switch statement be avoided if the return type is unknown by the calling method?

I have been reading a book called Clean Code A Handbook of Agile Software Craftsmanship. The author in the book motivates that a switch statement should be avoided and if it cannot be avoided it should be relegated to factory methods. I have a connection object which is receiving various PDUs (protocol data units). The PDUs vary and they can be received in any order. So if I have a method for example:
public BasePdu ReceiveNext();
because I cannot tell what the packet type is until it has been fully received. In the header of the PDU is an identifier as to which type it should be. This means that the calling method is going to have figure out what the type of the PDU is and based on that call the relevant method to handle it. This sounds like a perfect example for a switch statement. The object that contains the connection I would ideally like to have two threads. One for receiving PDUs and another for servicing a queue of PDUs to be sent.
Now I know that you cannot follow every bit of good advice and that there are just some circumstances which are the exception to the rule. Is this one of them? Or is there a way around this that I just have not yet thought of.
UPDATE:
I hear what a lot of people are saying by creating subclasses of response handlers. The issue is that the containing object has a lot of context and additional information that the handlers would need for example lookups and throttling etc etc. To inject all of this information into subclasses of handlers would be quite a chore to maintain and would also split a lot of logic up when it feels better to be encapsulated in the object that it is in now.
Personally I wouldn't worry about it too much; if it looks like a good place for a switch statement use one. On the other hand this also looks like a situation where you could use a factory method if each PDU type is handled by a class rather than a method. And, accoding to your book, you're allowed you to use switch statements then
Simply create a PDUParserFactory which create the parser based on a PDU type using switch statements on the PDU type identifier. This is the case where the book says it's ok :)
Update: One possible approach
class BasePDU
{
string Name { get; set; }
...
}
class PDUType1 : BasePDU
{
...
}
...
class PDUReceiver
{
public event EventHandler<PDUReceivedEventArgs> PDUReceived;
private void ParsePDU(byte[] data)
{
BasePDU pdu;
switch (byte[0]) // PDU type selector
{
.... parse PDU based on type
}
OnPDUReceived(pdu);
}
private void OnPDUReceived(BasePDU pdu)
{
var handler = PDUReceived;
if (handler != null)
{
handler(this, new PDUReceivedEventArgs(pdu));
}
}
}
Then you can attach listeners to the event:
pduReceiver.PDUReceived += BaseHandler;
pduReceiver.PDUReceived += PDUType1Handler;
...
void PDUType1Handler(object sender, PDUReceivedEventArgs e)
{
// only care about PDUType1
if (e.PDU.GetType() != typeof(PDUType1))
return;
....
}
Alternatively you could also create a event handler dictionary in the receiver, mapping a pdu type to event handlers and then let handlers register for specific types only. This way not all handlers would be called for each received PDU.
Instead of heaving a PDU type hierarchy you could also just have a:
class PDU
{
public PDUType PDUType { get; }
public byte[] PDUData { get }
}
then register handlers in the receiver for each PDUType and let the handler do whatever it wants with the data.
It's hard to give more concrete advice without knowing what exactly you want to do with your received packets.
If I understand your question correctly you have really two questions:
How to create the correct PDU's when you've received the name without using switch.
Create a simple factory by using a dictionary Dictionary<string, Func<PduBase>>
How the method calling public BasePdu ReceiveNext(); can handle it properly without using switch
Do not use a RecieveNext method. Create a AddPduHandler<T>(IPduHandler<T> handler) where T : PduBase method to the class receiving all PDU's. Store all handlers in a dictionary with the type as key: Dictionary<Type, Delegate>
Storing a delegate is kind of a trick since you can not work with the typed interface in the receiving class.
Update
This solution do not break Liskovs Substitution Principle which all implementations using switch do. This means that this class will work no matter how many different types of PDU's that you have.
It's also easier to test your application since each handler is isolated from everything else.
The bonus side is that everything is Typed (except the reader class) which will make it easier of find errors instead of working with casting magic or such.
public class Receiver
{
Dictionary<Type, MethodMapping> _handlers = new Dictionary<Type, MethodMapping>();
Dictionary<string, Func<PduBase>> _factories = new Dictionary<string, Func<PduBase>>();
// Small container making it easier to invoke each handler
// also needed since different generic types cannot be stored in the same
// dictionary
private class MethodMapping
{
public object Instance { get; set; }
public MethodInfo Method { get; set; }
public void Invoke(PduBase pdu)
{
Method.Invoke(Instance, new[] {pdu});
}
}
// add a method used to create a certain PDU type
public void AddFactory(string name, Func<PduBase> factoryMethod)
{
_factories.Add(name, factoryMethod);
}
// register a class that handles a specific PDU type
// we need to juggle a bit with reflection to be able to invoke it
// hence everything is type safe outside this class, but not internally.
// but that should be a sacrifice we can live with.
public void Register<T>(IPduHandler<T> handler) where T : PduBase
{
var method = handler.GetType().GetMethod("Handle", new Type[] { typeof(T) });
_handlers.Add(typeof(T), new MethodMapping{Instance = handler, Method = method});
}
// fake that we've received a new PDU
public void FakeReceive(string pduName)
{
// create the PDU using the factory method
var pdu = _factories[pduName]();
// and invoke the handler.
_handlers[pdu.GetType()].Invoke(pdu);
}
}
public interface IPduHandler<in T> where T: PduBase
{
void Handle(T pdu);
}
public class TempPdu : PduBase
{}
public class TempPduHandler : IPduHandler<TempPdu>
{
public void Handle(TempPdu pdu)
{
Console.WriteLine("Handling pdu");
}
}
public class PduBase
{ }
private static void Main(string[] args)
{
Receiver r = new Receiver();
r.AddFactory("temp", () => new TempPdu());
r.Register(new TempPduHandler());
// we've recieved a PDU called "temp".
r.FakeReceive("temp");
}
The reason to avoid swith statements are not because if structures are any better (when a switch is used, a bunch of if's will make it worse, not better) it mainly because the problem is not solved in an OO way.
From an OO point of view it is almost always better to use polymorphism then a switch statement.
In your example it's probably better to use a factorymethod to provide the appropriate handler for your type of package.
not sure if that's exactly the point, but having different instances whose treatment differs according to an ID is in fact a case for creating subclasses (the choice of subclass representing the information that was previously stored in the ID) of eg BasePdu and have the compiler figure out which method to use. if you're doing that by switching, it means you're not fully taking advantage of structuring your code by subclassing.

Categories

Resources