Is it possible in C# to overload a method with different signatures in different descendants of a base class, and then call the appropriate method based on the runtime type of an argument to the overloaded method?
As background, I'm working on message handling process, starting from a point where the message type not known at compile time, and with different message handling functionality intended to be added for different types over time. Ideally new message handling would change the subclass but not the base class. It works fine now by including all typed methods in the base class and explicitly selecting the correct one based on the message type. The question is driven mainly by curiosity since based on the answer to this question I expect it should be possible. If I'm overcomplicating this and theres a simpler approach I'd welcome that advice too.
Example code showing this is below, I expected HandleTypedMessage(BaseMessage msg) and HandleTypedMessage(MessageA msg) to be called once each, instead HandleTypedMessage(BaseMessage msg) is called twice.
class Program
{
static void Main(string[] args)
{
var msga = new MessageA();
var msgb = new MessageB();
MessageHandler handlerA = new MessageHandlerA();
handlerA.HandleTypedMessage((dynamic) msga);
handlerA.HandleTypedMessage((dynamic) msgb);
Console.ReadLine();
}
}
public interface IHandlesMessage
{
void HandleMessage(BaseMessage message);
}
public abstract class BaseMessage
{
public abstract string MessageType { get; }
}
public class MessageA : BaseMessage
{
public override string MessageType
{
get { return "http://message/a"; }
}
}
public class MessageB : BaseMessage
{
public override string MessageType
{
get { return "http://message/a"; }
}
}
public abstract class MessageHandler
{
public abstract void HandleTypedMessage(BaseMessage msg);
}
public class MessageHandlerA : MessageHandler
{
public override void HandleTypedMessage(BaseMessage msg)
{
Console.WriteLine("Did nothing with " + msg.MessageType);
}
public void HandleTypedMessage(MessageA msg)
{
Console.WriteLine("Handled message a");
}
}
public class MessageHandlerB : MessageHandler
{
public override void HandleTypedMessage(BaseMessage msg)
{
throw new NotImplementedException();
}
public void HandleTypedMessage(MessageB msg)
{
System.Console.WriteLine("Handled message b");
}
}
You're making incorrect part of your method invocation dynamic. Make the target dynamic, not the parameter:
((dynamic)handlerA).HandleTypedMessage(msga);
((dynamic)handlerA).HandleTypedMessage(msgb);
It will look for best matching HandleTypedMessage method for actual type handlerA points to (MessageHandlerA), not the one it's declared as (MessageHandler).
Code above prints
Handled message a
Did nothing with http://message/a
One approach is to move the dynamic dispatch into the handler sublcasses like this:
public class MessageHandlerA : MessageHandler {
public override void HandleTypedMessage(BaseMessage msg) {
this.HandleTypedMessageCore((dynamic) msg);
}
private void HandleTypedMessageCore(BaseMessage msg) {
Console.WriteLine("Did nothing with " + msg.MessageType);
}
private void HandleTypedMessageCore(MessageA msg) {
Console.WriteLine("Handled message a");
}
}
Related
I will admit, i am doing homework and i am stuck on this one question (Part A). How do i pass the notice method as reference to the railway signal ? Can't i just find out which class was called in the abstract constructor and then print the class name within the notify method? For example:
RailwayUser
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.Writeline(className);
}
RailwaySignal Class
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
This kind of code / design is flawed, since what it does is RailwayUser, registers the object reference with the _watchers List in the RailWaySignal class, which in turn calls the public Notice method on each user when Notify is invoked, which is not how Event Signaling or Function Pointer works. In fact public _watchers is dangerous, as it can be cleared by any user, though that can be moderated using property access
Code with Issue
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
Following shall be the actual code using events and delegates:
Correct Version
Code Snippet Online - https://www.jdoodle.com/embed/v0/uEc
void Main()
{
List<RailwayUser> railwayUsers = new List<RailwayUser>();
railwayUsers.Add(new RailwayUser());
railwayUsers.Add(new RailwayUser());
RailwayUser.TestNotification();
}
public enum Colour
{
Red,
Green,
NoSignal
}
public class RailwaySignal
{
public string Name {get; set;}
public RailwaySignal(string railwaySignalName)
{
Name = railwaySignalName;
}
// Delegate for handling event
public delegate void RailwaySignalEventHandler(object source, Colour e);
// Delagate object for handling event
private RailwaySignalEventHandler _railwaySignalEvent;
// Event Accessor
public event RailwaySignalEventHandler RailwaySignalEvent
{
add
{
lock (this)
{
_railwaySignalEvent += value;
}
}
remove
{
lock (this)
{
_railwaySignalEvent -= value;
}
}
}
// Invoke Event for subscribed clients
private void Notify()
{
if (_railwaySignalEvent != null)
_railwaySignalEvent.Invoke(this, Colour.Green);
}
// Test the Event Invocation
public void TestEvent()
{
Notify();
}
}
public class RailwayUser
{
private static RailwaySignal railwaySignal { get; set;} = new RailwaySignal("Signal1");
public RailwayUser()
{
railwaySignal.RailwaySignalEvent += this.Notice;
}
public static void TestNotification()
{
railwaySignal.TestEvent();
}
public void Notice(object sender, Colour color)
{
Console.WriteLine($"Notice Called, Colour is :: {color}, Sender is :: {((RailwaySignal)sender).Name}");
}
}
Result
Notice Called, Colour is :: Green, Sender is :: Signal1
Notice Called, Colour is :: Green, Sender is :: Signal1
Important Details
Signature of the event is, (object source, Colour e) which helps in passing the relevant information across to the RailwayUser called, We now know the RailwaySignal triggering the notification to the RailwayUser and its Colour value
Event / Delegate has same signature as called method (which is the basis of working of Delegate / function pointers)
For simplification RailwayUser is a non abstract class
Event is executed using Notify() method inside the RailwaySignal, we are calling it artificially using TestNotification() inside RailwayUser just for demo purpose, but ideally it shall be internally triggered and shall pass on current state like Colour
Pre-defined delegates like Func, Action are quite often used for similar notification mechanism, They internally works using similar mechanism, though declaring an explicit event which is internally a delegate is a well defined pattern, especially for the Ui controls
Standard events exposed by the .Net framework have the signature object sender, EventArgs e, where EventArgs can wrap all information from Event executor (RailwaySignal) to Event receiver (RailwayUser)
It seem like a Observer pattern.You can pass SubClass which inherit from RailwayUser object instance into RailwaySignal class
Your RailwayUser class need create public abstract void Notice(Colour state) method.
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
Driver class can inherit RailwayUser class then override Notice method.
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine($"Driver see the {state.ToString()}");
}
}
There are
List<RailwayUser> _watches contain observable object
use SubScript(RailwayUser user) subscription user on _watches List.
RailwayUser Notify() to invoke all your observable Notify method.
look like this.
public class RailwaySignal
{
private List<RailwayUser> _watches;
public Colour Stata { get; set; }
public RailwaySignal()
{
_watches = new List<RailwayUser>();
}
public void SubScript(RailwayUser user)
{
_watches.Add(user);
}
public void Notify()
{
foreach (RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(Stata);
}
}
}
sample:https://dotnetfiddle.net/GcdGMy
You can also use event to pass method into RailwaySignal then invoke Notify method.
public enum Colour
{
Green,
Red,
Disable
}
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Driver see the "+ state.ToString());
}
}
public class Controller : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Controller see the " + state.ToString());
}
}
public class RailwaySignal
{
public delegate void NoticeEvent(Colour state);
public event NoticeEvent Notifys;
public Colour Stata { get; set; }
public void Notify()
{
if (Notifys != null)
{
Notifys(Stata);
}
}
}
use like this.
RailwaySignal railway = new RailwaySignal() { Stata = Colour.Green};
railway.Notifys += new Driver().Notice;
railway.Notifys += new Controller().Notice;
railway.Notify();
sample : https://dotnetfiddle.net/GcdGMy
I am quite new to C# and I cannot understand the behaviour of a class in my project.
I am using an interface that defines a generic with a type constraint which is another interface.
When I call the generic, I know that a certain method exists on the argument (because of the type constraint), but this method doesn't get executed when I call it.
The only workaround I have so far is to include the method call into the type-specific method overloads.
This may be better explained with the following snippet with an equivalent type structure:
public interface ITrickable
{
void GetRabbitOut();
}
public interface IMagic
{
void DoTricks<T>(T obj) where T : ITrickable;
}
public class Hat : ITrickable
{
public void LiftUp() { Console.WriteLine("Lifting up the hat..."); }
public void GetRabbitOut() { Console.WriteLine("A rabbit came out the hat !"); }
}
public class Box : ITrickable
{
public void OpenDoubleBottom() { Console.WriteLine("Opening the box..."); }
public void GetRabbitOut() { Console.WriteLine("A rabbit came out the box !"); }
}
public abstract class Magician : IMagic
{
public abstract void DoTricks<T>(T obj) where T : ITrickable;
}
Now if I call DoTricks(new Hat()); DoTricks(new Box()); with the class below:
public class Houdini : Magician
{
public override void DoTricks<T>(T obj)
{
try {
DoTricks(obj); }
catch {
throw new NotImplementedException(); }
}
public void DoTricks(Hat obj)
{
obj.LiftUp();
obj.GetRabbitOut();
}
public void DoTricks(Box obj)
{
obj.OpenDoubleBottom();
obj.GetRabbitOut();
}
}
The output is as expected:
Lifting up the hat...
A rabbit came out the hat !
Opening the box...
A rabbit came out the box !
But if the class is defined as this one below:
public class Genesta : Magician
{
public override void DoTricks<T>(T obj)
{
try {
DoTricks(obj);
obj.GetRabbitOut(); } // <--- This seems to be ignored !?
catch {
throw new NotImplementedException(); }
}
public void DoTricks(Hat obj)
{
obj.LiftUp();
}
public void DoTricks(Box obj)
{
obj.OpenDoubleBottom();
}
}
The output is
Lifting up the hat...
Opening the box...
The question is why does GetRabbitOut is not called in the second class?
EDIT: The calling code is:
public static void Main(string[] args)
{
var houdini = new Houdini();
var hat = new Hat();
var box = new Box();
houdini.DoTricks(hat);
houdini.DoTricks(box);
Console.ReadLine();
}
Notice your method calls (I imagine it looked something resembling this):
Genesta g = new Genesta();
g.DoTricks(new Hat());
g.DoTricks(new Box());
Since you call g.DoTricks(new Hat()) rather than g.DoTricks<Hat>(new Hat()), no surprises that the exact method of the Genesta class that is invoked is DoTricks(T obj) and not DoTricks<T>(T obj). And when considering the implementation of DoTricks(T obj)...
public void DoTricks(Hat obj)
{
obj.LiftUp();
}
public void DoTricks(Box obj)
{
obj.OpenDoubleBottom();
}
The result is actually what you can expect from these methods!
If, however, you would call the generic method like this...
g.DoTricks<Hat>(new Hat());
You would fall into an infinite recursion, as the method would call itself indefinitely. DoTricks<T>(T obj) will always call itself and not one of the specialized overloads DoTricks(Hat) or DoTricks(Box), since the compiler cannot know before runtime that T will in fact be either Hat or Box.
By the way, the Houdini class experiences the same effect - it just so happens that its specific DoTricks(Hat) and DoTricks(Box) methods produce the result that you expected from calling DoTricks<T>(T obj).
I'm trying to implement a generic abstract method with a type constraint, then Implement it multiple times using different specified types.
public abstract class Ability
{
public abstract void BindToStation<T>(T station) where T : Station;
}
public class DashAbility : Ability
{
public override void BindToStation<NavStation>(NavStation station){ }
public override void BindToStation<CannonStation>(CannonStation station){ }
}
But I get an error which says the method has already been defined with the same paramater types.
I'm guessing that the compiler treats any generic paramater as the same in terms of the method signature, so these two methods look the same to it.
Still though, I'm wondering if theres a way to have generic method overloading using specific types.. ?
You can't do exactly what you want, but you can try an approach like this:
interface IBindableTo<T> where T : Station
{
void BindToStation(T station);
}
abstract class Ability
{
public abstract void BindToStation<T>(T station) where T : Station;
}
class DashAbility : Ability, IBindableTo<NavStation>, IBindableTo<CannonStation>
{
public override void BindToStation<T>(T station)
{
if (this is IBindableTo<T> binnder)
{
binnder.BindToStation(station);
return;
}
throw new NotSupportedException();
}
void IBindableTo<NavStation>.BindToStation(NavStation station)
{
...
}
void IBindableTo<CannonStation>.BindToStation(CannonStation station)
{
...
}
}
Hope this helps.
C# doesn't support specialization in that way, and neither does C++ easily when you want to specialize on runtime type.
But you can use polymorphism, so you can use double-dispatch:
public abstract class Station {
internal abstract void DashBindToStation();
}
public class NavStation : Station {
internal override void DashBindToStation() {
throw new NotImplementedException();
}
}
public class CannonStation : Station {
internal override void DashBindToStation() {
throw new NotImplementedException();
}
}
public abstract class Ability {
public abstract void BindToStation(Station station);
}
public class DashAbility : Ability {
public override void BindToStation(Station station) {
station.DashBindToStation();
}
}
Another possibility with C# is to use runtime dispatching using dynamic:
public abstract class Station {
}
public class NavStation : Station {
}
public class CannonStation : Station {
}
public abstract class Ability {
public abstract void BindToStation(Station station);
}
public class DashAbility : Ability {
public void BindToStation(NavStation station) {
}
public void BindToStation(CannonStation station) {
}
public override void BindToStation(Station station) {
BindToStation((dynamic)station);
}
}
Suppose I have the following interface:
public interface IMessageProcessor<T> where T: BaseMessage {
void Process(T msg);
}
I have an abstract class that implements this interface:
public abstract class AMessageProcessor<T> : IMessageProcessor<T> where T : BaseMessage {
protected Controller Controller { get; private set; }
public AMessageProcessor(Controller controller) {
Controller = controller;
}
public abstract void Process(T msg);
}
then I have a message:
public class RRMessage : BaseMessage {
...
}
and then I have an implementation:
public class RRMessageProcessor : AMessageProcessor<RRMessage> {
public RRMessageProcessor(Controller controller) : base(controller) {}
public override void Process(RRMessage msg) {
//do something here
}
}
Now in another class I would like to make a list of these processors for different messages:
public readonly List<AMessageProcessor<BaseMessage>> AvailableMessageProcessors;
public MessageProcessingStrategy(Controller controller) {
AvailableMessageProcessors = new List<AMessageProcessor<BaseMessage>>();
/* ----- ERROR HAPPENS AT THIS LINE ------ */
AvailableMessageProcessors.Add(new RRMessageProcessor(controller));
}
And I get this error:
Error CS1503 Argument 1: cannot convert from 'RRMessageProcessor' to
'AMessageProcessor<BaseMessage>'
Seems like that conversion should work... Why can't it convert? How can I get it to work?
I had a problem in my other answer (deleted) about covariant types in parameters but an approach like this may solves your problem:
Defines a BaseMessageProcessor class (could be AMessageProcessor) like this one:
public abstract class BaseMessageProcessor
{
protected Controller Controller { get; private set; }
public BaseMessageProcessor(Controller controller)
{
Controller = controller;
}
public void Process<T>(T msg) where T : BaseMessage
{
if (this is IMessageProcessor<T>)
(this as IMessageProcessor<T>).Process(msg);
throw new NotSupportedException();
}
}
Defines an interface IMessageProcessorOf<T>:
public interface IMessageProcessor<T> where T : BaseMessage
{
void Process(T msg);
}
Defines concrete processors inheriting of BaseMessageProcessor and implementing (explicitly) IMessageProcessorOf<T>:
public class RRMessageProcessor : BaseMessageProcessor, IMessageProcessorOf<RRMessage>
{
public RRMessageProcessor(Controller controller) : base(controller) { }
void IMessageProcessor<RRMessage>.Process(RRMessage msg)
{
...
}
}
This solution allows you to work with AvailableMessageProcessors:
public List<BaseMessageProcessor> AvailableMessageProcessors;
...
AvailableMessageProcessors = new List<BaseMessageProcessor>();
AvailableMessageProcessors.Add(new RRMessageProcessor(controller));
So, if you have 2 messages types like RRMessage and SSMessage, you can define one MultiMessageProcessor:
public class MultiMessageProcessor : BaseMessageProcessor, IMessageProcessorOf<RRMessage>, IMessageProcessorOf<SSMessage>
{
public MultiMessageProcessor(Controller controller) : base(controller) { }
void IMessageProcessorOf<RRMessage>.Process(RRMessage msg)
{
...
}
void IMessageProcessorOf<SSMessage>.Process(SSMessage msg)
{
...
}
}
The calls to Process() method will be made through BaseMessageProcessor.Process<>:
multiProcessor.Process(new RRMessage());
multiProcessor.Process(new SSMessage());
Or just use RRMessageProcessor and define a SSMessageProcessor using the same idea like before.
This isn't the cleanest/prettiest way to do it but it seems to work. Here's what I changed in AMessageProcessor:
public abstract class AMessageProcessor : IMessageProcessor<BaseMessage>
protected Controller Controller { get; private set; }
public AMessageProcessor(Controller controller) {
Controller = controller;
}
/* ----- REALLY DON'T WANT TO OVERRIDE THIS METHOD EVERYWHERE --- */
public abstract void Process(BaseMessage msg);
}
And then changing the RRMessageProcessor as:
public class RRMessageProcessor : AMessageProcessor, IMessageProcessor<RRMessage> {
public RRMessageProcessor(Controller controller) : base(controller) {}
/* ----- REALLY DON'T WANT TO OVERRIDE THIS METHOD LIKE THIS EVERYWHERE --- */
public override void Process(BaseMessage msg) {
Process(msg as RRMessage);
}
public void Process(RRMessage msg) {
//do something here
}
}
On your generic type you'll need to define the type as covariant. See https://msdn.microsoft.com/en-us/library/dd997386.aspx
This should work
public interface IMessageProcessor<in T> where T : BaseMessage
{
void Process(T msg);
}
You can easily solve your problem by redefining
public readonly List<AMessageProcessor<BaseMessage>> AvailableMessageProcessors;
into
public readonly List<RRMessageProcessor> AvailableMessageProcessors;
So you don't have cast problems and you're using the you're custom objects
hope to help!
Suppose you had such code:
public Base
{
abstract void Register();
}
public Registrator1: Base
{
override void Register()
{
//uses the current state of the object to populate the UI captions
}
}
public Registrator2: Base
{
override void Register()
{
//uses the current state of the object to populate the UI captions
}
}
But When you receive a new business rule asking you to write Registrator3 which actually registers based on some parameter and you change your code base to the next:
public Base
{
abstract void Register(externalParam);
}
public Registrator1: Base
{
override void Register(externalParam)
{
//uses the current state of the object to populate theUI
}
}
public Registrator2: Base
{
override void Register(externalParam)
{
//uses the current state of the object to populate the UI
}
}
public Registrator3: Base
{
override void Register(externalParam)
{
//uses a DDD - service passed in the params to populate the UI
}
}
But Registrator1 and Registrator2 do not need that param and the code becomes smelly. What are the ways to re-write this code?
You could use an object as a parameter here; which is commonly used in scenarios where the number of parameters can vary depending on the call being used.
struct RegistrationInfo
{
public static readonly RegistrationInfo Empty = new RegistrationInfo();
public string Username;
public string CustomerName;
public string Validity;
}
abstract class Base
{
public abstract void Register(RegistrationInfo info);
// If you want to retain the paramaterless call:
public void Register()
{
Register(RegistrationInfo.Empty);
}
}
class Registrar1 : Base
{
public override void Register(RegistrationInfo info)
{
if (info.Username == null) throw new ArgumentNullException("info.Username");
}
}
class Registrar2 : Base
{
public override void Register(RegistrationInfo info)
{
if (info.CustomerName == null) throw new ArgumentNullException("info.CustomerName");
}
}
This has the advantage that you don't need to change method parameters (which is breaking interface) each time a parameter is added. The usage also becomes somewhat self-documenting:
var r = new Registrar1();
r.Register(new RegistrationInfo(){ Username = "JimJoe" });
r.Register(RegistrationInfo.Empty);
It's like air freshener for this type of code smell, while it's still smelly; you can make it smell nicer.
Finally you can make the call-site cleaner by making it a params argument (this has a small amount of overhead); in all honesty though it is more smelly because it's a language hack. Finally you could improve it with generics:
class RegistrationInfo
{
}
class RegistrationInfo1 : RegistrationInfo
{
public string Arg;
}
class RegistrationInfo2 : RegistrationInfo
{
public int Arg;
}
interface IBase<in TRegistration>
where TRegistration : RegistrationInfo
{
void Register(TRegistration registration);
}
class Base : IBase<RegistrationInfo>
{
public void Register(RegistrationInfo registration)
{
}
}
class Registrar1 : IBase<RegistrationInfo1>
{
public void Register(RegistrationInfo1 arg)
{
}
}
class Registrar2 : IBase<RegistrationInfo2>
{
public void Register(RegistrationInfo2 arg)
{
}
}
Is it not possible to contain the logic for externalParam in Registrator3?
In other words, Registrator3 uses the param, then calls the unmodified parameterless base?
A lot really depends on where the logic belongs. If it is something intrinsic to the base, then put it in the base, and either overload the Register() function or supply a default value for the param so that sub classes don't need to provide it.
Assuming you want to reuse the registration logic from the base class, you could update the code as follows:
public class Base
{
public virtual void Register(object externalParam)
{
// base registration logic goes here
}
}
public class Registrator1: Base
{
public override void Register(object externalParam)
{
base.Register(null);
// custom registration logic goes here
}
}
public class Registrator2: Base
{
public override void Register(object externalParam)
{
base.Register(null);
// custom registration logic goes here
}
}
public class Registrator3: Base
{
public override void Register(object externalParam)
{
base.Register(externalParam);
// custom registration logic goes here
}
}
HTH,
Cosmin
EDIT: Updated code to compile.