This is a .NET 1.x implementation detail that has been incredibly difficult to get rid of. Part of the problem must be that the majority of books about .NET were written before 2005, they all have a chapter about finalizers and they all have it dead wrong.
Roughly, if you think you need a finalizer then you're wrong 99.9% of the time. That finalizable resource needs to be wrapped in its own class and that class needs to have a finalizer. If you think you need to implement the Disposable pattern then you're wrong ~95% of the time. Definitely wrong here. But sometimes you don't have a choice if your base class already made the mistake of implementing it. Several of the .NET Framework classes have this mistake, a problem that Microsoft couldn't fix anymore.
The that class with the finalizer in the previous chapter is not one you should implement yourself. It was already written, .NET 2.0 acquired the SafeHandle
classes. You, at best, might need to derive your own class if the Release() method isn't one that's already provided in the framework. SafeBuffer is useful for pointer-based resources.
The big, big difference is that these classes are pretty special, they have critical finalizers. Which is an expensive word that means that they are guaranteed to run, even if the finalizer thread bombed on an unhandled exception. Not actually that important in the majority of the LOB apps, if they go down in flames then the OS cleanup is usually good enough. But essential when .NET code runs in a host that has a long uptime guarantee. SQL Server is a good example, having to reboot it because too much SQL/CLR code bombed and leaked handles is bad for business.