EDIT2: So it is clear that the class hierarchy here is a bit overdone. To simplify it, I could do
abstract class SenderReceiver {
Socket _Socket;
protected class StateObject { /* For async network methods */ }
void Dispose();
void Exit();
}
class SenderReceiverTcp : SenderReceiver {
void Connect() { /* Sets up socket and calls BeginConnect */ }
void EndConnect() { /* Callback for Connect() */ }
void Accept() { /* Sets up socket, Bind, Listen, BeginAccept */ }
void EndAccept() { /* Callback for Accept() */ }
void Send(msg) { /* Send message */ }
void Receive() { /* Receive or BeginReceive */ }
void EndReceive() { /* Callback if Receive is async */ }
}
class SenderReceiverUdp : SenderReceiver {
void Connect() { /* Sets up socket and calls Bind */ }
void Send(msg) { /* Send message */ }
void Receive() { /* Receive or BeginReceive */ }
void EndReceive() { /* Callback if Receive is async */ }
}
class SenderRecevierD1 {
SenderReceiverTcp _SenderReceiverTcp;
void Connect() { _SenderReceiverTcp.Connect(); }
void Accept() { _SenderReceiverTcp.Accept(); }
void Send() { msg.Create(); _SenderReceiverTcp.Send(msg); }
...
}
class SenderReceiverD2 {
SenderReceiverUdp _SenderReceiverUdp;
void Connect() { _SenderReceiverUdp.Connect(); }
void Send() { msg.Create(); _SenderReceiverUdp.Send(msg);
_SenderReceiverUdp.Receive();
if (!_SenderReceiverUdp.ReceiveEnded()) // Check for ack
_SenderReceiverUdp.Send(msg);
}
...
}
This way, the D# classes don't need to know how to send messages for their type (maybe one D class with TCP retries if there is no acknowledgement while another doesn't wait for an acknowledgement because that D type doesn't send acknowledgements) but the differences in TCP/UDP setup and send are abstracted in the SenderReceiverTcp/Udp classes.
This is sort of an extension of "Removing SenderReceiver from the base class". I don't want to post this as an answer because I'm not positive if it's maximally right. I'll leave it for a bit and wait for community feedback.