The hierarchy of channels derives from the single interface IChannel. By itself, IChannel is not particularly interesting because it doesn’t introduce any new methods for communication. Each channel shape, such as IInputChannel or IDuplexSessionChannel, has its own interface derived from IChannel that describes the capabilities and semantics of the channel.
When you write a channel, you may support variations of your channel for many different channel shapes. For example, you may support both an IInputChannel and an IOutputChannel variation for sending and receiving. Or, you may support both an IDuplexChannel and IDuplexSessionChannel variation depending on whether sessionful communication is needed. During the channel build process, your channel factory or listener will be asked to supply a single particular variation of IChannel. Since the variation is part of the generic contract, the concrete channel implementation that gets returned by the channel factory or listener must support the specified interface.
Here are two best practices that specify more explicitly how that concrete channel implementation should work.
- Your channel factory or listener can’t substitute an extended variation of a channel shape even though the type system lets you cast to the parent type. For example, IDuplexChannel and IInputSessionChannel both derive from IInputChannel. It would be wrong to return a duplex or sessionful channel if asked for an IInputChannel though. The build process is asking for the semantics of a one-way, non-sessionful input channel rather than just a type that has a callable interface compatible with IInputChannel. Use separate shape-changing channels if you need to adapt a channel implementation or supply a channel implementation that natively supports the correct shape.
- Each concrete channel implementation should only implement a single channel shape. While it’s possible to write a single implementation that simultaneously supports multiple channel interfaces, you should instead have multiple channel implementations each supporting a single channel interface. The fact that you have different channel implementations for different shapes is hidden by the channel factory or listener so there’s no additional complexity that the user of your implementation needs to worry about.
Next time: Extensibility