Will be more easy to post the code first then ask why I'm getting this error.
abstract class Packet
{
// base class!
}
public sealed class FirstPacket : Packet
{
// First packet implementations...
}
public sealed class AnotherPacket : Packet
{
// Another packet implementations...
}
public enum OpCode
{
FirstPacket,
AnotherPacket
}
public abstract class BaseConnection
{
private Dictionary<OpCode, Action<Packet>> _packetHandlers;
public Connection() {
_packetHandlers = new Dictionary<OpCode, Action<Packet>>();
}
}
public sealed class Client : BaseConnection
{
public Client() : base() {
// Here will throw the errors...
// CS1503 Argument 2: cannot convert from 'method group' to 'Action<Packet>'
_packetHandlers.Add(OpCode.FirstPacket, OnReceiveFirst);
_packetHandlers.Add(OpCode.AnotherPacket, OnReceiveAnother);
}
public void OnReceiveFirst(FirstPacket packet) {
}
public void OnReceiveAnother(AnotherPacket packet) {
}
}
According to this answer, a derived class is an instance of its base class and no casting involved.
In my code, if both FirstPacket
and AnotherPacket
is Packet
, why I have to "cast" using lambda?
public sealed class Client : BaseConnection
{
public Client() : base() {
// This works...
_packetHandlers.Add(OpCode.FirstPacket, p => { OnReceiveFirst((FirstPacket)p); });
_packetHandlers.Add(OpCode.AnotherPacket, p => { OnReceiveAnother((AnotherPacket)p); });
}
public void OnReceiveFirst(FirstPacket packet) {
}
public void OnReceiveAnother(AnotherPacket packet) {
}
}
It doesn't make sense to me.
First, note that your lambdas
p => { OnReceiveFirst((FirstPacket)p); }
would not compile without a cast.
The reason why you can do a cast is that you know enough about the logic of your system to decide that OnReceiveFirst
would never be called with a parameter of SecondPacket
. Hence you conclude that the cast is safe.
Compiler, on the other hand, cannot conclude the same thing, so it asks you to supply a cast manually.
Method groups provide a shortcut for situations when no casting is necessary. For example, if you rewrite your OnReceiveFirst
like this
public void OnReceiveFirst(Packet packetOrig) {
FirstPacket packet = (FirstPacket)packetOrig;
...
}
you would be able to use it with method group syntax:
_packetHandlers.Add(OpCode.FirstPacket, OnReceiveFirst); // Compiles
Here, too, casting remains your responsibility, in the sense that if the cast throws an exception, you will be able to trace the error to your own code, not to some compiler magic.