What you may want to keep in mind is that callers may assume that the value of a property does not change unless it has been explicitly assigned. This isn't necessarily a smart assumption, but nevertheless it is one that will be made.
Many programmers would expect the following test to pass:
var b1 = foo.Bounds;
var b2 = foo.Bounds;
Assert.AreEqual(b2, b1);
Now, for something like a Rect
, this probably isn't a major problem, especially if it's immutable (as a Rect
normally would be) and either a value type or has a value-equality operator. In this case, it really doesn't matter if two different calls get two different instances because it's very difficult to write code that implicitly depends on them being the same instance.
Also, lazy instantiation is almost always fine:
public Rect Bounds
{
get
{
if (bounds == null)
bounds = GetBounds(...);
return bounds;
}
}
...because subsequent calls will get the same instance, barring any multi-threading problems.
But things can get hairy when a property actually returns a different instance every time, and that instance is a fairly complex and especially mutable object.
For example, let's say you return a Stream
:
public Stream OutputStream
{
get { return new FileStream(...); }
}
This is fairly horrible because someone's very likely to write code like:
foo.OutputStream.Write(data1, 0, data1.Length);
foo.OutputStream.Write(data2, 0, data2.Length);
And depending on exactly how you've implemented OutputStream
, one of two nasty things will happen, either there will be a file-locking exception on the second call, if OutputStream
always references the same file, or the caller might unintentionally write data to two different files (if, let's say, OutputStream
actually creates a new file each time), subsequently corrupting both files.
Don't expect callers to save the return value of a property. They often won't. So if you're considering writing a property that returns a different object each time, make sure that there aren't any serious risks to having two instances floating around at the same time and possibly being swapped or lost.
I've spent many a painful hour debugging code where it looked like everything was working correctly but it turned out I was only working on a copy of the correct instance. Iterators with deferred execution are an especially pernicious subset of this problem; it's not uncommon to foreach
through it once and change some property of each element, then foreach
through it again and find that everything has "reset"!
One last thing - you should never new up an object that requires cleanup from a property accessor. There's almost no chance at all that the caller will clean it up.