Shawn has the coolest blog. Minimalist, low-noise, quality, and always funny content. I just read his reasons you might use nested classes.
I would have sent this offline (via SMTP, about as 'offline' as I get these days) except I'm on a workstation with no email client while I watch chkdsk chug away on my other machine.
So, since I have a spare FOUR HOURS* I thought I'd mention number 110:
110: Exposing public interfaces via private implementations.
Prototypical example: System.Collections.Hashtable.Synchronized( .. )
That method returns Hashtable. But Hashtable is a non-sealed public class which is just basically an implementation impaired public interface. What gets returned from the Synchronised(..) method is typically “more“ than Hashtable. “More“ in the sense of implementation inheritance. What is returned *is* a Hashtable, but it's also *more than* a Hashtable because it also supports synchronisation. ( I'm being a bit sloppy there, clearly synchronisation is something that the Hashtable interface actually does cater for, it's just that the method of implementing that support can be deferred, which is what this is all about )
That's another pretty good example of a place where you might like to use a private nested class. To avoid complexity of the public API and to avoid complex specialization of the 'standard' Hashtable implementation. I.e. in the normal course of events, the Hashtable just does it's thing with no regard for synchronisation concerns and simply implements IsSynchronized as return false. Then all complexity ( and expense! ) of synchronisation can be deferred to the private wrapper derived from Hashtable ( managing complexity ), but at the same time the class definition of the synchronised wrapper need not be part of the standard public ( nor internal ) API ( reducing complexity ). Such an implementation leaves you in a position where you can avoid an interlocked compare on every public call to see if you should be synced or not, on one hand it sounds like it might be a performance hack, but on the other hand it sounds like a pretty decent way to keep code out of your code path ( less complex == good ).
I think it's probably the best example of why you might use a private nested classes.
( I also nest private flags / enum definitions that are used to represent internal state. )
In short, I think there are cases where nested classes can help you manage complexity. Often in the case of managing public API complexity while exploiting implementation inheritance, and also too in simply managing implementation complexity ( i.e. putting functionality where it belongs, ala: functional cohesion. ) I can see why you might be inclined to be cautious of it though, because used inappropriately nested classes can actually significantly increase complexity, because you lose encapsulation of the 'containing' class. ( assuming there is some definition of encapsulation in OO that allows for the possibility of nested classes! ;)
John.
p.s. “Parameterised Properties“ are EVIL!
p.p.s. Public nested classes are almost certainly a bad idea. I can't think of many reasons you'd want to do that, apart from deliberately ruining encapsulation for some ungodly reason and at the expense of increased public API complexity..
* Might not be four hours. Batteries not included. This statement does not reflect the views of my company.
I concur wholeheartedly on the nested classes. I use nested classes in places where I have 5000+ lines of code and want to encapsulate some of that code that will be instaniated at a particular point in the parent class, but I only use private nested classes. I never expose any of them, cause I think thats a really, really bad idea and makes things more complex than they have to be.
However, why do you think parameterized properties are evil? I have to code some of my libraries in vb, and I kind of like them. Things like the following are nice to have:
Private mWidgets(42) As Widget
Public Property Widgets(ByVal i As Integer) As Widget
Get
Return mWidgets(i)
End Get
Set(ByVal value As Widget)
mWidgets(i) = value
End Set
End Property
or
Private mPixels(10, 10) As Pixel
Public Property Pixels(ByVal row As Integer, ByVal column As Integer) As Pixel
Get
Return mPixels(row, column)
End Get
Set(ByVal value As Pixel)
mPixels(row, column) = value
End Set
End Property
p.s. Don't you like indexers in C#? Same type thing as a parameterized property in VB.
Indexers aren't the same thing, indexers target a *class* not a *property*.
I had this argument a while ago over here:
http://groups.yahoo.com/group/win_tech_off_topic/message/30166
Ok, I understand that indexers target a class, as it defines the default property for a class. However, the only way to define a default property for a class in VB is to use a parameterized property, therefore making the default parameterized property a target of a class as well. I agree that any other parameterized property not declared as the indexer/default in VB targets a property and not the class. I was referring more to the default parameterized property in VB, which is the same thing as the indexer in C#.
Still, don't you find that something like the widgets properties I posted would be useful?
Nope.
If you want a widgets collection, then use a collection class! If you don't want a collection, then use a method!
I think having a widgets collection could be useful.
You would use a DictionaryBase for the class, and use a parameterized property as the default to pull the specific item of the dictionary via the key passed as the parameter to the property. This is how most indexers/default properties get implemented. Place the word Default in front of the Widgets property above, and now you have the indexer for the Widgets class, which inherits DictionaryBase and holds a collection of different instantiations of the class widget. mWidgets would be the private field collection of those widget classes. Use Widgets(i) to return the widget class via the key for the dictionary. Of course, there are numerous ways to do these things, I just prefer this way, and therefore have good use for indexers/default properties.