Delphi RAD libraries implement many thread friendly objects in a way which forces one to declare another object for storing the reference returned by that object's locking method. The object thus essentially encapsulates the prime object which is returned on locking. For example;

List := ThreadList.Unlocklist;
// do something with List
ThreadList.LockList;


Why is this approach taken? Why is inheriting from the prime object, adding the locking object, overriding constructors and destructors, discouraged? For example the following implementation adds a TMREWSync to a list;  

IMREWS = interface

  ['{5B6DE5FA-847B-42D5-8BF4-9EB20A452C54}']

  procedure BeginRead;
  function  BeginWrite: Boolean;
  procedure EndRead;
  procedure EndWrite;

end;

TThreadList = class ( TList, IMREWS )
private

  FLock : TMREWSync;

public

  constructor Create;
  destructor  Destroy; override;

  property Lock : TMREWSync read FLock implements IMREWS;

end;

constructor TThreadList.Create;
begin
  FLock := TMREWSync.Create;
  inherited;
end;

destructor TThreadList.Destroy;
begin
  inherited;
  FLock.Free;
end;

// Usage:

ThreadList.BeginWrite;
// Do something with ThreadList
ThreadList.EndWrite;


In my experience, its best to throw exceptions at the point where the errors occur.  You do this because it's the point where you know the most about why the exception was triggered.

As the exception unwinds back up the layers, catching and rethrowing is a good way to add additional context to the exception. This can mean throwing a different type of exception, but include the original exception when you do this.

Eventually the exception will reach a layer where you are able to make decisions on code flow (e.g a prompt the user for action). This is the point where you should finally handle the exception and continue normal execution.

With practice and experience with your code base it becomes quite easy to judge when to add additional context to errors, and where it's most sensible to actually, finally handle the errors.

Catch → Rethrow

Do this where you can usefully add more information that would save a developer having to work through all the layers to understand the problem.

Catch → Handle

Do this where you can make final decisions on what is an appropriate, but different execution flow through the software.

Catch → Error Return

Whilst there are situations where this is appropriate, catching exceptions and returning an error value to the caller should be considered for refactoring into a Catch → Rethrow implementation.
