When, why and where should a struct be used?

  • Thread starter Thread starter clintonG
  • Start date Start date
Lucian Wischik said:
My original observation was that, yes, there is language support to
demonstrate that a function returns you an "immutable" thing, in the
sense that the function will no longer alter it -- make it return a
struct! So that counts (as per thread title) of an instance "when why
and where" a struct should be used. It's for when your design calls
for something immutable by the sender, and you want it robustly
compiler-supportedly immutable against the possibility of later
programmer mistakes. (and sometimes just when the weight of putting
every single field into the constructor is too much...)

Except that if your struct contains a mutable reference type variable,
the data within that could change. It won't refer to a different
object, but the data within the object could change.

Note that using structs doesn't help when you want to return more than
one value - such as a collection. If you return an array of value
types, for instance, the array could still change contents behind the
scenes.
I think it's interesting that, if a function returns IEnumerable<T>,
then it gives a compiler-supported guarantee that the *recipient* will
not subsequently alter the list. But it's impossible for any choice of
interface to make similar guarantees about the *sender*.

True. Documentation is key here, IMO.

I agree it's an issue - but it's one which hasn't proved to be too
worrying in real life, IMO. It's like the type-safety given by generic
collections - it's more useful from a confidence, automatic
documentation and value type efficiency standpoint than to actually get
rid of bugs, because in my experience there aren't many times when you
*actually* try to use something in (say) an ArrayList and choose the
wrong type.
 
My original observation was that, yes, there is language support to
demonstrate that a function returns you an "immutable" thing, in the
sense that the function will no longer alter it -- make it return a struct!

True (with caveats noted by Jon). The only difficulty here is that
making something a struct changes its behaviour radically, and so yes,
you have gained a guarantee of immutability, but you've lost reference
semantics in the process. Depending upon the situation, that can be
like smashing the antique piggy bank to get at the dollar inside.

Like Jon, I've not yet run across a situation in which it was so
important to _guarantee_ immutability (in this sense) that it was worth
resorting to value semantics in order to do so. However, there is
always more to learn, and perhaps I'll run across such a situation in
the future, and I'll remember this discussion. Thanks. :-)
 
Bruce Wood wrote:
....
Well, yes, but there's another way that the Dispose method is called,
and that's by the garbage collector just before it reclaims an object.
So, it's not _entirely_ necessary that Dispose be called on an object
before it goes out of scope... it's just better practice because it
releases unmanaged resources sooner. (At least, that's my
understanding... someone please correct me if I'm wrong.)

Is it not Finalize method which is called by GC? To my understanding
(which can also be wrong indeed), GC does not call Dispose, so this is
still necessary to call it somehow. Normally, the call to Dispose is
placed in the object destructor for exactly this very reason: if it
was not called directly, like with 'using', it is guaranteed that it
will be called during garbage collection.
 
Sericinus hunter said:
Is it not Finalize method which is called by GC? To my understanding
(which can also be wrong indeed), GC does not call Dispose, so this is
still necessary to call it somehow. Normally, the call to Dispose is
placed in the object destructor for exactly this very reason: if it
was not called directly, like with 'using', it is guaranteed that it
will be called during garbage collection.

Yes, Finalize is called by the GC. The pattern is to have two Dispose
methods, one taking a boolean indicating whether it is being called via
the other Dispose method (true), or the Finalize / destructor method
(false).

And classes that own unmanaged resources *directly* are the *only*
classes which should have a finalizer.

-- Barry
 
Bruce Wood wrote:
...

Not true always, what about SqlConnection which lies in a connection
pool, not calling Dispose (or Close) on it will cause the connection to
remain open. It is not garbage collected as the pool hold a reference to
it so it cannot be used again.
Is it not Finalize method which is called by GC? To my understanding
(which can also be wrong indeed), GC does not call Dispose, so this is
still necessary to call it somehow. Normally, the call to Dispose is
placed in the object destructor for exactly this very reason: if it
was not called directly, like with 'using', it is guaranteed that it
will be called during garbage collection.

Finalize does not automaticaly call Dispose, but anything implementing
IDisposable should have the constructor call Dispose, or perform the
same function as Dispose (this is after all the function of the
finalizer).

From the docs on SqlConnection "If the SqlConnection goes out of scope,
it is not closed. ". This would imply to me that the finalizer for
SqlConnection does not close the underlying connection. I suppose this
must be the case as you also must not access other objects from a
finalizer (it may be that the underlying connection has been GCed
already and the wrapper is GCed after it, you cannot garuntee the order
of garbage collection).

Thus whilst finalize should clean up any unmanaged resources it cannot
do anything for managed resources. If one of those managed resources
happens to live in a pool or cache then you have the possibility of a
memory/resource leak.
 
Chris said:
Finalize does not automaticaly call Dispose, but anything implementing
IDisposable should have the constructor call Dispose, or perform the
same function as Dispose (this is after all the function of the
finalizer).

Yes, that's what I was trying to say.
And I hope, by 'constructor' you actually meant 'destructor'.
 
And I hope, by 'constructor' you actually meant 'destructor'.

DOH!

Heh, the new anti-pattern of constructing disposed objects, now I've
only to find a use for it...
 
Back
Top