Oftentimes, .net objects will ask other entities (which may or may not even be on the same computer) to "do something"(*) on their behalf, and such entities will continue doing so unless or until they are told to stop. Such objects should implement IDisposable, and their IDisposable.Dispose routine should notify any and all entities which had been doing something on their behalf, to stop doing so. If all references to an IDisposable object were to disappear without Dispose being called first, some other entities might continue forever in uselessly doing something on behalf of an object which has long since ceased to exist.
(*) That "doing something" could be anything, including blocking other requests to do something. For example, an object might ask the operating system for exclusive access to a file, and the operating system might relay that request to another computer. If the object doesn't notify the OS when it no longer needs access to the file, the server could leave everyone else in the universe locked out of the file indefinitely.
In an effort to minimize problems from entities' perpetually acting on behalf of abandoned objects, .net provides a means by which objects can ask to be notified when they're abandoned. If an object which overrides Object.Finalize() is abandoned, then .net will usually call that object's override of the Finalize() method. This kinda sorta works, mostly, but it should almost never be relied upon. It's very hard to design a class so that Finalize() will always do the right thing and never do the wrong thing. Among other things, if one isn't careful, it's possible for .net to call Finalize() on an object which it determines is going to be abandoned, even while that object is interacting with an outside entity. This will never happen in code which properly calls Dispose on the object, but may happen in code which relies upon Finalize().