I've been reading about WeakPointer
and WeakPointer<T>
today, and it looks very useful. Rather than just using it as-is though, I decided to write a wrapper around it that covers a common usage case for me.
Code as follows:
public class Temporary<T> where T : class
{
Func<T> generator = null;
WeakReference<T> reference = null;
public T Value
{
get
{
return GetValue();
}
}
public Temporary(T value)
: this(value, null)
{ }
public Temporary(Func<T> generator)
: this(null, generator)
{ }
public Temporary(T value, Func<T> generator)
{
reference = new WeakReference<T>(value);
this.generator = generator;
}
public T GetValue()
{
if (reference == null)
return null;
T res;
if (!reference.TryGetTarget(out res) && generator != null)
{
res = generator();
reference.SetTarget(res);
}
return res;
}
}
I've tested this against my use-case and it works as I expect - the garbage collector cleans out items with no active references, and they are re-instantiated at need.
I'm looking for critiques of the implementation and suggestions for improvements/additions.
1 Answer 1
It's pretty good, clean and simple code. Not a lot to critique really. A few minor things:
Some people (myself included) prefer to prefix private fields with
_
. This way it's easy to see that it's a field rather than a local variable (and gets rid of thethis.
in most cases).You shouldn't have a public property and a public method which do the same thing. How is a programer supposed to know whether to use
Value
orGetValue
? It's not obvious what the difference is or if there is one at all.Not 100% sure about the
null
check inGetValue()
. There is no code path wherereference
should ever benull
. So it would actually indicate a bug if it were the case which you are hiding with this check. Consider removing the check or actually throwing anInvalidOperationException
or similar instead. Detecting bugs early is important. Another option would be to look at Code Contracts to state the invariants of that method.I have started to try and avoid
null
checks where possible by making sure that there is always an object. In your case this could be something like this:public class Temporary<T> where T : class { private static Func<T> NullGenerator = () => null; Func<T> generator = NullGenerator; WeakReference reference = null; public T Value { get { return GetValue(); } } public Temporary(T value) : this(value, NullGenerator) { } public Temporary(Func<T> generator) : this(null, generator) { } public Temporary(T value, Func<T> generator) { if (generator == null) throw new ArgumentNullException("generator"); reference = new WeakReference(value); this.generator = generator; } private T GetValue() { T res; if (!reference.TryGetTarget(out res)) { res = generator(); reference.SetTarget(res); } return res; } }
-
1\$\begingroup\$ I'm not sure about
NullGenerator
, it makes the code simpler, but I also think it makes it less clear. The explicit check againstnull
in the original version is very clear. \$\endgroup\$svick– svick2013年12月06日 10:59:31 +00:00Commented Dec 6, 2013 at 10:59 -
\$\begingroup\$ +1 for the null generator and rewriting GetValue() around nulls. Exactly what I would have said. That null check on reference was certainly a bug. Also, could not agree more with item 2. \$\endgroup\$tallseth– tallseth2013年12月06日 13:21:41 +00:00Commented Dec 6, 2013 at 13:21
-
\$\begingroup\$ Good answer Chris. The
reference == null
check was a leftover from when I was thinking about implementingIDisposable
, but it shouldthrow
if there's a problem with the reference. I agree with @svick about the NullGenerator, but the rest of your points are excellent. \$\endgroup\$Corey– Corey2013年12月07日 09:06:13 +00:00Commented Dec 7, 2013 at 9:06