I see some classes in the .net framework that support reading and writing, saving and loading, classes whose main responsibility is not them, for instance the Stream classes and the XML Dom/XLinq classes.
Let's touch on these one by one.
S is for Single Responsibility
- The single responsibility for XMLDocument is to provide APIs for traversing an XML document
- Yes, its interface has methods for saving and loading, but the implementation of XMLDocument doesn't actually save or load. It parses or unparses with respect to a abstraction, a "stream," which you'd have to supply to the XMLDocument in order for those methods to work. Parsing and unparsing is naturally associated with traversal. Persistence isn't handled by the class itself.
- Stream and StreamReader/Writer classes also do this. They work on logical representations, a theoretical infinite byte array, which is actualized in one of the subtypes, such as a
O is for open/closed
Moot. All framework classes are closed to modification, unless you are doing something really weird.
L is for Liskov Substitution
Pretty sure you can substitute any type of stream when a steam is needed; whoever is using the stream generally won't care if it's a stream that goes to memory, to disk, or to a network connection. If there are differences in capability (e.g. a maybe a network stream doesn't provide random access) those capabilities are discoverable via properties in the base class, e.g.
I is for Interface Segregation
Microsoft has been a bit slow on providing interfaces for many of its classes. That being said, you can easily write code that works with abstract base types instead of concrete types, e.g. you can write code that works with any type of stream as long as it inherits from
D is for Dependency Inversion
XMLDocument and the stream classes both use dependency injection, following the principle of inversion of control. In fact StreamReader and StreamWriter are two of the earliest examples of this principle, since they take the underlying stream as a constructor argument (via a stream's abstract base implementation, which is as good as accepting an interface). XMLDocument uses DI by allowing an abstract base stream to be injected as a method argument to Save or Load.
So no, I don't agree with you, I think the .NET framework does a decent job of following SOLID.