What I wouldn't give to have this work:
public interface ICallBack
{
void Handle<T>(T arg);
}
public class MessageHandler : ICallBack
{
public void Handle<T>(T arg)
{
string name = typeof(T).Name;
Console.WriteLine(name);
}
public void Handle(int arg)
{
string name = "wow, an int";
Console.WriteLine(name);
}
}
public class Worker
{
public void DoSomething(ICallBack cb)
{
cb.Handle(55);
}
}
//...
Worker foo = new Worker();
ICallBack boo = new MessageHandler();
//I want this to print "Wow, an int"
foo.DoSomething(boo)
Unfortunately, it calls the generic entry point rather than the "specialized" entry point. Well, thats interfaces for you.
I've also tried the same approach but replacing the int-specific signature with a generic signature that is Mojo specific:
public void Handle<T>(T arg) where T : Mojo {}
I was hoping this would be sufficient to form a "special override" if the argument was of type Mojo. But now the compiler complains that I have two methods with the same signature (one that is Mojo specific, the other open-ended). Well, I was actually hoping it would think it was "the same signature" so that both would fulfill the interface and the "best" would be selected at run-time. Ah well.
In effect, I'm trying to achieve is vaguely similar to "Traits," which are the "else-if-then of C++". I guess it could also be considered a form of "interface signature contravariance."
I'd love to discover there is a special C# keyword that enables this capability, or that it is a featured addition to C# in .net 4.5.
Yes, no? Comments?
No this is not possible.
When the compiler compiles the type implementing the interface, it will create an interface map detailing which methods of the type that is linked to each method of the interface. This cannot be changed at runtime at will.
This means that whenever you call your Handle method through that interface, it will always go to the same method on the underlying type, regardless of any other methods you feel should be more appropriate.
If you want the underlying type to call specific methods internally, depending on the specific type of the generic parameter, you will have to implement that yourself, either using dynamic dispatch, or by using if-statements or similar to detect which type of T you have and call the appropriate method.
The answer here that says you can cast the type you're calling the method on to dynamic means you're using reflection to bypass the interface alltogether. The interface might as well not have any methods at all for this particular scenario, the cast to dynamic will still "work".
I don't recommend this approach. You're effectively writing code that assumes it has carte blanche access to all methods of the underlying type, even though it specifically says "I only need this interface".
Additionally, if the only goal was to avoid runtime errors, consider what will happen if you implement the method explicitly in the class:
void Main()
{
Worker foo = new Worker();
ICallBack boo = new MessageHandler();
foo.DoSomething(boo);
}
public interface ICallBack
{
void Handle<T>(T arg);
}
public class MessageHandler : ICallBack
{
void ICallBack.Handle<T>(T arg)
{
string name = typeof(T).Name;
Console.WriteLine(name);
}
}
public class Worker
{
public void DoSomething(ICallBack cb)
{
((dynamic)cb).Handle(55);
}
}
This will crash at runtime with:
RuntimeBinderException:
'UserQuery.MessageHandler' does not contain a definition for 'Handle'
You can test the above code in LINQPad.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With