I think I've finally got it.
Finalize is called by the GC. It should not be called by a user. But if the class implements IDisposible, it should call Dispose(true). It does not automatically call Dispose; this is only done if the user overrides Finalize.
Probably it's better to use the ~Foo() finalize method, since this automagically calls Base.Finalize, which ought to be done.
A web site I found says that if a program implements either Finalize or Dispose, it should implement both, although it should not implement either unless there is a very good reason (in particular, freeing memory from a module of unmanaged code).
I had been worried that GC.SuppressFinalize would somehow leave an object in memory. This is not true. In fact, the SuppressFinalize method signals the CLR that the given instance doesn't need to have its Finalize method executed, because the work has already been done. It should be used if an explicit, non-GC call to Dispose has already taken care of cleanup.
This site helped to clarify things for me: http://www.gotdotnet.com/team/libraries/whitepapers%5Cresourcemanagement%5Cre...