The Delphi language shares with many others a standard resource allocation pattern to make sure that in case of an
exception the resources is properly released. Resources in this context are memory objects, files, operating system objects and handles, and the like. In Delphi, compared to other languages with a garbage collector, the relevance increased by memory management considerations.
Protecting a Single Object
In the most simple cases, you'd write code like:
allocate resource
try use resource
finally free resource
end; A more specific example would be like:
A1 := TTest.Create;
try A1.DoSomething;
finally A1.Free;
end; So far, so good. Notice that is an error happen during the constructor execution, Delphi will automatically*execute the destructor for the partially initialized object (but this could be a topic for another blog post).
Protecting Two Objects: How NOT to Write the Code
The issue I want to focus on is how to write the same type of code if you need*to allocate and dispose two resources. Here there are multiple options. What you shouldn't do (but is fairly common) is to write:
A1 := TTest.Create;A2 := TTest.Create;try A1.DoSomething; A2.DoSomething (A1);finally A2.Free; A1.Free;end; The issue with this code is that in case the creation of A2*fails (and there could be many reasons), the A1*object would remain in memory. Simply pushing the second allocation within the try block is also not good:
A1 := TTest.Create;try A2 := TTest.Create; A1.DoSomething; A2.DoSomething (a);finally A2.Free; A1.Free;end; With this code*in case of a failure in object A2*constructor call, the finally block will try to
Free an uninitialized object (the default value of a local object reference is undefined). This is why a possible solution is to set A2*to nil at the beginning -- as calling Free on a nil object has no effect. Or set all object reference to nil for simplicity and uniformity. Or*write two nested try blocks to protect each of the resources.
Protecting Two Objects: A Tale of Three Solutions
This long*introduction brings us to the point of this blog post. There are at least 3 different correct solution for the issue of protecting two resources in the same code block, as I just mentioned. Here are the three solutions in an image I "
borrowed" from one of our
RAD Studio R&D architects,*Bruneau Babet.
Provided they are all correct in terms of proper resource management in all scenarios, which are the advantages and disadvantages of these 3 solutions? What is important to consider is that the 2*resources could*be 3, or 4 or half a dozen.*
Which One to Pick?
The first solution with the nested try blocks fairly clean but more verbose (more lines of code) and*has additional nesting that could become an annoyance with multiple resources. Also, there is a runtime cost associated with try blocks, clearly limited but not zero.*
The second solution has the least amount of lines of code code and the least amount of runtime cost. The only additional is setting A2 to nil. The code remains readable also with many resources. However, the code is "unbalanced" and it might be slightly confusing.
The third solution goes in the same direction, but it adds one extra technically*useless assignment to nil (for A1), offering the advantage of being cleaner and more balanced, and likely more readable after all.
So what's the best solution? This is really hard to tell,. I personally mostly used #1 in my books,*but at Embarcadero we tend to prefer the cleaner and faster ones (that is, #2 or #3) for library code. Interested in your opinions, of course.
Weiterlesen...