This project is read-only.

Visualizing dependencies

Jul 6, 2011 at 11:20 PM

I thought I should start a new thread for this. My coworker is finding numerous UC-related bugs where things don't update properly. One problem is the WinForms ListBox as we have discussed before, but there are other, more mysterious problems. I would like to debug them with some kind of visualization.

So I was proposing earlier a simple textual summary:

* Model.PersonList
   * Person.FullName[x3]
     * Person.FirstName[x3]
     * Person.LastName[x3]

I'm not sure how to present this summary tree within a debugger. Currently this article's approach won't work because Precedents are not serializable. But, moving on, assuming the list of persons has 3 items, an expanded version would be useful too:

* Model.PersonList
   * Person.FullName=Joe Johnson
     * Person.FirstName=Joe
     * Person.LastName=Johnson
   * Person.FullName=Brock Braun
     * Person.FirstName=Brock
     * Person.LastName=Braun
   * Person.FullName=Patricia Trie
     * Person.FirstName=Patricia
     * Person.LastName=Trie

My first thought is that this expanded version could take advantage of the built-in debugger UI by replacing the default debugger view, as explained in my article about visualizing IList<T>. The problem is, the user sometimes wants to see precedents (things that this Dependent depends on) and other times the user wants to see Dependents (things that this Precedent depends on). To resolve this, you could simply offer a "Dependents" property in Precedent that returns a tree of dependents, plus a "Precedents" property in Dependent that returns a tree of dependents and independents.

Come to think of it, both kinds of trees (summary and expanded) could be offered with properties. You could offer "DependentsSummary" and "PrecedentsSummary" properties that return summary trees instead of expanded trees.

Now the technical problem with the expanded version is that if the user uses Independent<T>, there is no link from the Independent to its value. Can you think of any solution? I've decided I like the idea of making Independent<T> a derived class of NamedIndependent (and similarly for Dependent<T>) for this purpose, since it's cheaper memory-wise than the current implementation of Independent<T>... but of course it's a breaking change. Alternately, you could define new names for these derived-classes-with-T, NamedIndependent<T> and NamedDependent<T>, to avoid breaking existing code. I know you said in the other thread,

"I don't think that inheritance is the appropriate relationship for Independent<T> or Dependent<T>. These classes live at a higher level of abstraction than the sentry classes. This would be considered implementation inheritance rather than Liskov inheritance."

But implementation inheritance is the most straightforward solution to the visualization problem, and it works without needing to enable a special "debug" flag. I think I'll hack something together and send it to you. Sound good?

Jul 7, 2011 at 8:22 PM
Edited Jul 7, 2011 at 8:35 PM

Okay! Check this out! I added four classes derived from Independent/Dependent: NamedIndependent, NamedIndependent<T>, NamedDependent and, of course, NamedDependent<T>. I implemented three classes for debugger visualization, which are returned by the Uses, UsesSummary, and UsedBy. All precedents have UsedBy, but only Dependents have Uses and UsesSummary. Then I edited all the WinForms controls to use NamedIndependent and NamedDependent instead of Independent and Dependent. Finally I edited DependentList, IndependentList and IndependentDictionary to instantiate NamedDependent or NamedIndependent and give the sentry a useful name.

I made a little demo app which models a list of persons and their ages, see here:

It has a checkbox that hides people under 18 years old (sentry name: Form1.ShowAdultsOnly) and another one that causes age to be shown in the list (Form1.ShowAgeInList). I made a picture showing how the three visualization classes look in the debugger:

Note the difference between "listPersons._depItemText.Uses" and "listPersons._depItemText.UsesSummary". The second one is useful when a list has a large number of items, as it collapses multiple items into a single row. Conceivably I could make a UsedBySummary property too, though it seems less important.

I noticed while making the demo app that the ListBox (which shows the list of persons) would not update correctly, just like the ListBox in my main application. Replacing the ListBox with ListView fixed the problem, but the problem is that ListView requires me to define columns with a fixed length (even if I hide column headers), which is problematic if the ListView is supposed to be resizable. So whenever I don't need columns, ListViews are more hassle than ListBoxes.

Note: this Visualization system requires the VS debugger option "Call string-conversion function on objects in variable windows" to be enabled.

Edit: BTW, the "#12345" suffixes are displayed for dependents and independents that don't have an associated value (i.e. they are not NamedIndependent<T> or NamedDependent<T>). This is simply the last 5 hex digits of the hash code so the developer can tell apart different precedents.

Jul 7, 2011 at 9:30 PM

Now that's cool!

The source control server is down, but please check in your Named classes when it's back up again. I'd like to get a closer look at how those work.

Jul 7, 2011 at 11:39 PM
Edited Jul 7, 2011 at 11:42 PM

Oh... so I'm on the team? :) Okay, but what about the existing Independent<T> and Dependent<T>? I mean, right now I added NamedIndependent<T> separately from Independent<T>. But that's kind of a silly design. Ideally Independent<T> itself would support visualization, right?

BTW, when telling users about this feature, be sure to explain why

  1. all the precedents of a dependent disappear when the dependent is updated. That was baffling until I realized why it was happening. Now I'm just baffled why one of my Dependents still has one dependency left over when the Update() method is called. Shouldn't they all disappear?
  2. all the dependents that use an independent disappear when that independent is changed.

Indeed, one of these two situations is typically in play whenever the user is stopped in the debugger, so it must be understood. Perhaps the visualizer could help by appending "out of date" to its description (for Dependents). Independents have no status code, so I'm not sure we can help the user in case of (2).

P.S. Note that I'm relying entirely on manual naming so far (the magic tricks we discussed are still doable).

P.P.S. UpdateControls.dll is now bloated from 19K to 25.6K :O

P.P.P.S. I made the debugger properties protected, as they are not intended for user source code; but maybe they should be public instead so that the debugger allows IntelliSense (IntelliSense doesn't list non-public members.)

Jul 8, 2011 at 2:22 PM

Yes, you are on the team. I thought I could push some of the work off on you. Congratulations. :)

Actually, I think the idea of keeping the Named sentries separate from the unnamed ones is a reasonable design. It lets you choose whether to take on the overhead. The drawback is that the controls would use one or the other of the (Named)Dependents, so you don't get to make the choice there. Maybe there's a way to turn it on across the app domain without incurring much overhead when it's off?

It's true that all precedents should disappear when a dependent goes out-of-date. Maybe you were stopped in the debugger as the dependent was updating, and it already wired up one dependency? At any rate, I can see how this would make debugging difficult. Ideally, you would see the steady state in the debugger. Maybe the summary could hold on to the former list of precedents while the dependent is out-of-date.

Jul 8, 2011 at 6:50 PM

Oh, I wouldn't worry about overhead in the controls. Named(In)Dependent is one word larger than (In)Dependent. So the overhead is only significant when you have a million or so. And if (In)dependent<T> is derived from Named(In)dependent, total memory use is 2 words smaller than the current design. Only one copy of the actual names will exist in memory, if they are plain strings in source code.

If the user wants the names to be computed automatically, that's mainly a time overhead (because the name, once constructed, can be interned), so they should have to turn on a debug flag to get auto-naming. I suppose we can walk the stack during OnGet() as you proposed; however, the stack will not always have the desired property name on it; a given class may call OnGet() in someplace other than a property getter (if there even is a property getter for that sentry!).

Maybe that same debug flag could enable caching of the last known first-level list of Dependent.Uses and the last known Independent.UsedBy. However, in the latter case it is not clear when to discard the cache, since AFAIK an Independent cannot know when its Dependents are fully wired up again. All it knows is when all Dependents are discarded (OnSet()). In addition, the user may want to see the current state rather than the cached state, so we'd have to offer a way to choose.

The Dependent.GetCurrentUpdate() method bugs me. Shouldn't it just be a property? To me, using a GetXyz() function instead of a property connotes an expensive operation.

Jul 8, 2011 at 9:21 PM
Edited Jul 8, 2011 at 10:02 PM

I just realized, there's a simple way to build the name of Dependents automatically... given the update method delegate, "update", use

	update.Method.DeclaringType.Name, update.Method.Name))

I don't know how much time that takes, but it's not too slow it could be enabled by default. Certainly it's cheaper than stack analysis (needed for Independents). But the result may be weird for lambda functions.

Edit: yes! I know. It should be enabled by default, but actually calculating the name can be deferred until someone calls ToString(), VisualizerName() or the Name property

Jul 23, 2011 at 1:12 AM

I have now done as I proposed, replacing Independent<T> and Dependent<T> with the contents of the classes that I originally named NamedIndependent<T> and NamedDependent<T>, so that Independent<T> is derived from NamedIndependent and Dependent<T> is derived from NamedDependent. Automatic naming seems to be working correctly, although I haven't tested it specifically (most of my precedents are named explicitly).

I wrote a new collection class, DependentDictionary<TKey,TValue>. It proved a bit tricky to get right (it didn't work correctly in my program on the first try).... hopefully it trips the dependent sentry correctly in all cases now. Only the TValues are recycled.

Also, today I changed RecycleBin as I proposed, to avoid that unnecessary overhead of Recyclable<T>. In addition, the list of _duplicates is created only if T is IDisposable AND duplicates actually exist. I also added an AddRange() method and a constructor that calls it. Finally, I didn't think that the documentation gave the reader a useful sense of what RecycleBin is for, so I rewrote most of it to specifically talk about models and viewmodels and how RecycleBin fits in the typical scenario.

I don't really think that what the Recycle() extension method does is immediately obvious. In particular, the fact that it clears the collection is not obvious. Maybe it should be called RecycleAndClear()? Or maybe it should be a static method or constructor of RecycleBin instead of an extension method.

I haven't got around to running the tests or unit tests yet.

Jul 24, 2011 at 6:22 PM

I never have liked the Recycle extension method. I find that I end up doing things manually instead.

One small request. Could you use checkin comments? It would make it easier to review.

Thanks. I'll put together a release soon.