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

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).

Related

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

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.

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);
}

Restrict Return Type of Generic Delegate Irrespective of Parameters

I'm trying to restrict the return type of a generic delegate without specifying the parameter signature, and I don't know how to do this, or if it is even possible.
How can I do this or is it impossible?
My research has come up dry. Some pseudo-C# code would probably help steer you toward what I'm trying to do:
public class SomeClass< T, U > where T : Delegate // returning U
{
private someDelegate;
public SomeClass( T someDelegate )
{
this.someDelegate = someDelegate;
}
public U Run()
{
return someDelegate.DynamicInvoke();
}
}
... Elsewhere
public delegate string aDelegate();
public static string SayHi()
{
return "Hello!";
}
aDelegate greeter = SayHi;
var something = new SomeClass< aDelegate, string>( greeter );
Console.WriteLine( something.Run() ); // Should write "Hello" to the console.
I know this is a rather contrived pseudo example. I aim for a more involved usage, of course. I'm trying to write a console menu class that would associate a list of given menu options with actions that would fire depending on what option the user chooses. Right now, it just returns the string the user chose from the menu. What I'd like be able to do is return what--if anything--the associated method returns. This could perhaps be returned with the user chosen option string in a tuple... But, I figured this mini-example just cut straight to the technical hurdle I'm experiencing.
Thanks!
.NET already defines a generic delegate that returns the generic argument as it's result, Func<T>. You don't even need to define it.
public class SomeClass<U>
{
private Func<U>;
public SomeClass(Func<U> someDelegate)
{
this.someDelegate = someDelegate;
}
public U Run()
{
return someDelegate();
}
}
There's no real useful reason to allow a user of the type to provide any arbitrary delegate of the same signature. Really I'd advise you to avoid using any delegates in your code other than Func and Action (with various different numbers of generic arguments) whenever possible, as it's just creating a hassle to do so. I would consider it a reasonable restriction on any caller that has a delegate of a different type but the same signature to simply convert it to a Func<T> anyway, it's not like it's even a difficult conversion for them.
If you don't want to use Func<T> (as Servy suggests), e.g. if you have a custom delegate of your own that you want to be passed with type-safety, then perhaps you can make your custom delegate generic with respect to its return type. Then you can do it this way:
public delegate T MyDelegate<T>();
public class Foo<T>
{
private readonly MyDelegate<T> _delegate;
public Foo(MyDelegate<T> handler)
{
_delegate = handler;
}
public T Bar()
{
return _delegate();
}
}

What is the purpose of delegates in .NET

So I'm a little bit confused about delegates in C#.... what do they do and how are they useful? I've read a few tutorials, and I don't really get exactly what they're supposed to do (everyone relates them to function pointers in C, and I've never programmed in C).
So... what do delegates do? What's a scenario in which I should use them? How would I then use them?
The other answers are good, but here's another way to think about delegates that might help. Imagine that a delegate is nothing more than an interface. When you see:
delegate void Action();
think:
interface IAction
{
void Invoke();
}
And when you see:
Action myAction = foo.Bar;
think:
class FooBarAction : IAction
{
public Foo Receiver { get; private set; }
public FooBarAction(Foo foo)
{
this.Receiver = foo;
}
public void Invoke()
{
this.Receiver.Bar();
}
}
...
IAction myAction = new FooBarAction(foo);
And when you see
myAction();
think
myAction.Invoke();
The actual details of what types get constructed are a bit different, but fundamentally that's what's happening. A delegate is simply an object with a method called Invoke, and when you call that method, it calls some other method on some other object on your behalf. That's why it's called a "delegate" -- because it delegates the call to another method of another object.
Delegates are sort of like objects that represent a method call. One useful way they can be used are as callbacks. For example, imagine you have a method that does something asynchronous, and you want the caller to be able to specify what they want to happen once it completes (Action is a type of delegate):
public void DoSomething(Action whatToDoWhenDone)
{
// Your code
// See how the delegate is called like a method
whatToDoWhenDone();
}
A user of DoSomething can now specify the callback as a parameter:
public void AnotherMethod()
{
DoSomething(ShowSuccess); // ShowSuccess will be called when done
}
public void ShowSuccess()
{
Console.WriteLine("Success!");
}
You can also use lamba expressions as a shorter way of writing your delegate:
public void AnotherMethod()
{
DoSomething(() => Console.WriteLine("Success!"));
// Also DoSomething(delegate() { Console.WriteLine("Success!"); });
}
Callbacks are far from the only use cases for delegates. Hopefully this shows you some of their power: the ability to have code to be executed as a variable.
Delegates allow you to treat functions as if they were any other variable. A delegate type defines the signature of the function, that is, what the function returns, and the number and type of arguments that it takes:
// This is the delegate for a function that takes a string and returns a string.
// It can also be written using the framework-provided Generic delegate Func, as
// Func<String, String>
delegate String StringToStringDelegate(String input);
You can define a variable of this type, and assign it to an existing method. I use the generic as an example, because that is the more common usage in .net since 2.0:
String Reverse(String input) {
return input.Reverse();
}
Func<String, String> someStringMethod = new Func<String, String>(Reverse);
// Prints "cba":
Console.WriteLine(someStringMethod("abc"));
You can also pass functions around this way:
String Reverse(String input) {
return input.Reverse();
}
String UpperCase(String input) {
return input.ToUpper();
}
String DoSomethingToABC(Func<String, String> inputFunction) {
return inputFunction("abc");
}
var someStringMethod = new Func<String, String>(Reverse);
// Prints "cba":
Console.WriteLine(DoSomethingToABC(someStringMethod));
var someOtherStringMethod = new Func<String, String>(UpperCase);
// Prints "ABC":
Console.WriteLine(DoSomethingToABC(someOtherStringMethod));
In a big application it is often required to other parts of the application based on some condition or something else. The delegate specifies the address of the method to be called. In simple manner a normal event handler implements the delegates in the inner layers.
The oversimplified answer is that a delegate is basically a "pointer" to a block of code, and the benefit is that you can pass this block of code into other functions by assigning your block of code to a variable.
The reason people relate Delegates to C function pointers is because this is in essence what delegation is all about, I.e.: Pointers to methods.
As an example:
public void DoSomething(Action yourCodeBlock)
{
yourCodeBlock();
}
public void CallingMethod()
{
this.DoSomething(
{
... statements
});
this.DoSomething(
{
... other statements
});
}
There are naturally lots of ways to invoke delegates as all of the tutorials will show you. The point is though that it allows you to "delegate" functionality in such a way that you can call into methods without necessarily knowing how they work, but simply trusting that they will be taken care of. In other words, I might create a class that implements a "DoSomething()" function, but I can leave it up to someone else to decide what DoSomething() will do later on.
I hope that helps. :-)
Delegates are a way to call back into your code when a long running operation completes or when an event occurs. For example, you pass a delegate to a method that asynchronously downloads a file in the background. When the download is complete, your delegate method would be invoked and it could then take some action such as processing the file's contents.
An event handler is a special type of delegate. For example, an event handler delegate can respond to an event like a mouse click or key press. Events are by far the most common type of delegate. In fact, you will typically see the event keyword used far more often in C# code than the delegate keyword.
You can think of it as a type in which you may store references to functions. That way you can in effect, store a function in a variable so you may call it later like any other function.
e.g.,
public delegate void AnEmptyVoidFunction();
This creates a delegate type called AnEmptyVoidFunction and it may be used to store references to functions that return void and has no arguments.
You could then store a reference to a function with that signature.
public static void SomeMethod() { }
public static int ADifferentMethod(int someArg) { return someArg; }
AnEmptyVoidFunction func1 = new AnEmptyVoidFunction(SomeMethod);
// or leave out the constructor call to let the compiler figure it out
AnEmptyVoidFunction func2 = SomeMethod;
// note that the above only works if it is a function defined
// within a class, it doesn't work with other delegates
//AnEmptyVoidFunction func3 = new AnEmptyVoidFunction(ADifferentMethod);
// error wrong function type
Not only can it store declared functions but also anonymous functions (i.e., lambdas or anonymous delegates)
// storing a lambda function (C#3 and up)
AnEmptyVoidFunction func4 = () => { };
// storing an anonymous delegate (C#2)
AnEmptyVoidFunction func5 = delegate() { };
To call these delegates, you can just invoke them like any other function call. Though since it is a variable, you may want to check if it is null beforehand.
AnEmptyVoidFunction func1 = () =>
{
Console.WriteLine("Hello World");
};
func1(); // "Hello World"
AnEmptyVoidFunction func2 = null;
func2(); // NullReferenceException
public static void CallIt(AnEmptyDelegate func)
{
// check first if it is not null
if (func != null)
{
func();
}
}
You would use them any time you needed to pass around a method that you wish to invoke. Almost in the same way that you may pass instances of objects so you may do what you wish with them. The typical use case for delegates is when declaring events. I have written another answer describing the pattern so you can look at that for more information on how to write those.

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