Skip to main content

C# Tutorial - Weak References [Advanced]


We all know (hopefully) that C# is a garbage-collected language. In general, what this means is that we as programmers don't need to free our own memory - the garbage collector will free that memory for us once it is no longer being referenced. Now, of course, garbage collection is a lot more complicated than that, and writing a good garbage collector is actually a relatively hard problem. And the fact that writing a perfect garbage collector is probably impossible is the reason why things like C#'s Weak Reference object exist.

Generally, when you talk about a reference to an object in .NET (and in most other garbage collected languages), you are talking about a "strong" reference. As long as that reference exists, the garbage collector won't collect the object behind that reference. A weak reference is a special type of reference where the garbage collector can still collect the underlying object, even though you still technically have a reference to it.

The key here is to remember that the garbage collector is not running all the time. As far as we, the programmers of an application, know it is completely random and could kick in at any time. This means that an object only referenced through a weak reference could sit around for long time, or for virtually no time at all (and really, it is even more complicated than trying to figure out the next time the garbage collector will run - because the garbage collector for C# is generational). And, as soon as you copy the reference out of the weak reference variable into a regular reference, the underlying object will no longer be collected (assuming that it hadn't already been collected), because now you have a strong reference to it.

Ok, enough with this theoretical talk. Let's get down to some code, and hopefully we can show how this weak reference object is actually useful.
public string _FilePath = "PathToMyImportantFile.dat";
public WeakReference _FileWeakRef = new WeakReference(null);

public List<double> ImportantBigFileContents
{
  get
  {
    List<double> fileStrongRef = _FileWeakRef.Target as List<double>;

    if (fileStrongRef == null)
    {
      using (StreamReader r = new StreamReader(_FilePath))
        fileStrongRef = ParseImportantData(r);

      _FileWeakRef.Target = fileStrongRef;
    }

    return fileStrongRef;
  }
}
 
Say I had a large chunk of external data that would be handy to keep in memory, but really isn't used very often (or maybe it is used a bunch in bursts). This is exactly what the weak reference object is good for. In the code above, I am storing the parsed version of some "Important Data" in a WeakReference variable. What this means is that when someone tried to access the ImportantBigFileContents, the parsed data may or may not still be in memory.

So first, we try and pull the reference out of the _FileWeakRef object. If that is null, we load the data from the file, parse it, store it and hand it back. Otherwise we hand back what we got from the weak reference variable. So this means that sometimes, the data will be in memory, but other times the code will have to go out and reload it. This doesn't make sense to do in all cases (or even in many cases), but if the data is accessed in bursts, and you really didn't want to keep it in memory all the time anyway, this gives you what you need with very little extra work (the garbage collector does your management for you).

Now, there is a couple of common tear-your-hair-out mistakes that can be made when using weak references. See if you can tell what is wrong with the code below (and remember, the garbage collector might run at any time):
public List<double> ImportantBigFileContents
{
  get
  {
    if (_FileWeakRef.Target == null)
      using (StreamReader r = new StreamReader(_FilePath))
        _FileWeakRef.Target = ParseImportantData(r);

    return _FileWeakRef.Target as List<double>;
  }
}
 
Figure it out? Yup, the garbage collector could run during this property access - cleaning up the memory for this weak ref object just as we were about to hand it back:
public List<double> ImportantBigFileContents
{
  get
  {
    if (_FileWeakRef.Target == null)
      using (StreamReader r = new StreamReader(_FilePath))
        _FileWeakRef.Target = ParseImportantData(r);

    /* Garbage colllector could run right here.  Whoops!*/

    return _FileWeakRef.Target as List<double>;
  }
}
 
So always remember to pull the reference out into a strong (or regular) reference before you do any manipulation or checking - otherwise, stuff could change out right from under you.

Well, that's all for an intro to the weak reference object. For you Java developers out there, you actually have an equivalent (and it works almost exactly the same) - the WeakReference class.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as