Jon Skeet [C# MVP] <sk***@pobox.comwrote:
Barry Kelly <ba***********@gmail.comwrote:
How do you get rid of the modifications in release-mode code, or toggle
them safely, in order to avoid the downsides to interfaces & virtual
methods?
What downsides of interfaces are you thinking of?
I tend to write libraries / runtimes / frameworks rather than
applications. That means that if you "ship" interfaces, you get all the
maintenance side-effects of interfaces. A good discussion is in
Framework Design Guidelines by Cwalina and Abrams, section 4.3. Here are
some downsides:
* Brittleness - add a method to an interface and you must modify all the
classes that implement it. If you have exactly one interface per class
to create replaceability, there's nothing to stop other people from
implementing the interface, which paradoxically increases inadvertent
and unwanted coupling.
* Double-sided semantics - this is the same problem as virtual methods.
There is one set of semantics for the user of a given interface, how it
is supposed to manipulate the object. Then, there's another set of
semantics for the implementer of the interface: what sort of method
calls it should expect, what order, etc.
* Lack of constructors - one must introduce factories or some other
indirection, adding complexity where it mightn't otherwise be needed.
* Lack of any visibility other than public - often, to get classes
working well together, members with internal visibility are required.
For example, consider IDataReader, IDbCommand and related interface
implementations. When you call ExecuteReader() on an IDbCommand, the
IDataReader you get back needs a communication channel with the
underlying connection etc. How does one test the individual classes that
make up such a system of classes without exposing the implementation
details to the world, thus losing encapsulation, and possibly even
security?
* Lack of discoverability - if you've got a complicated arrangement of
classes, where there are properties, parameters and collections of
interface types, it can be awkward to figure out which classes are the
appropriate implementations of a given interface. It's fine for common
interfaces like IList and IBindingList, but I remember more complicated
COM scenarios where I was never sure if there was some stock
implementation I was supposed to provide, or whether I was supposed to
implement the interface myself. It was even worse when the interfaces
were parceled out by function, because then there was no help from the
syntax to tell you whether or not you could safely QueryInterface (aka
cast) for another interface.
On a totally unrelated side point, I've had some success with using
IServiceProvider for this QueryInterface-like functionality, especially
for some of the more esoteric kinds of QI like tearoffs or conditionally
implemented interfaces. I have a Cast method which falls back to the
object instance itself if the query for IServiceProvider fails. It's a
nicely dynamic way of reducing coupling as well as providing an avenue
for using composition / mixin-style behaviour, since the instance
returned needn't be the same as the object you're querying.
-- Barry
--
http://barrkel.blogspot.com/