Empty Pattern

This thread on Msdn was one I came across, and I felt it needed a much better answer. His question in short, is what is the difference between null, Empty, and "" in C#. I assume that you know what null and "" are, so I'm going to focus on the meat of the question. So the obvious question is what is Empty? Let's look deeper into some existing types that use this pattern.

System.String
String value = String.Empty;

System.Drawing.Rectangle
Rectangle value = Rectangle.Empty

I could go on, but I will stop there. There are a few things that types using this pattern have in common.

  • They are types that have value semantics
  • Empty is a public static readonly field
  • Empty's type is the same as its containing type

Okay.. but what does all this mean. Empty is a public static readonly field that represents the default value for an instantiated type that has value semantics. This pattern is useful for two reasons. One, for value types, because value types are not nullable, it is an easy way to have access to the default value of that type, and is equivelent to calling default(T). And secondly, it is useful to find out if an instance of a type has been modified from its default value. Let's take a deeper look at how this pattern applies to Value Types and Reference Types, and we'll start with a value type.

public struct Point {
    public static readonly Point Empty = default(Point);
    public int X { get; set; } 
    public int Y { get; set; } 
}

For value types, since they cannot be null, it is good practice to use default(T) for declaring the empty field, however new Point() works just as well. Remember that default(T) for structs will return each member of the struct initialized to zero or null depending on whether they are value or reference types. For nullable value types, default returns a System.Nullable, which is initialized like any struct.

Point p1 = Point.Empty;    // {0,0}
Point p2 = new Point();    // {0,0}
Point p3 = default(Point); // {0,0}

All three lines of code are equivelent, except in regards to performance. Using Point.Empty we are not instantiating a new instance of Point, but the other two methods do. A minor detail, but important to note regardless.

Point p = Point.Empty;
p.X = 1;
p.Y = 1;

if (p != Point.Empty) {
    p.X = 0;
    p.Y = 0;
}

Now you can see how Empty is useful. Very easy to see if the point has been modified from its default value. We can see how useful this pattern is for structures, and how its used in existing types in .NET. What about reference types? The same practices apply, but there is a difference in how we should declare the static field, because reference types can be null, which is their default value.

public class Point {
    public static readonly Point Empty = new Point();
    public int X { get; set; } 
    public int Y { get; set; } 
}

In the above sample, we changed our Point from a value type to a reference type, but note line two. Instead of using default(T), we are using new Point(). This is because the default value for a reference type is null, which is great, but we don't want the default value, we want its Empty value.

The next important change, is that we must make the type immutable. Using this pattern on a mutable reference type will have negative impact and wrong behaviors in your application. Remember that value types use copy semantics, reference types hold a reference to the underlying object in memory, so if we were to use the following code:

Point p = Point.Empty;
p.X = 2;

This would actually modify the value of Point.Empty to be {2,0}. For this pattern to be viable when using a reference type, it must be immutable.

public class Point {
    public Point(int x, int y) {
        X = x;
        Y = y;
    }
    public static readonly Point Empty = new Point();
    public int X { get; private set; } 
    public int Y { get; private set; } 
}

There's not much else to say, hopefully by now you understand why this pattern exists, so let's recap my DO's and DO NOT's for this pattern.

DO Implement an T.Empty field on types that have value semantics.
DO Make T.Empty public static readonly.
DO Use default(T) as the value for T.Empty when used with a struct.
DO NOT Use default(T) as the value for T.Empty when used with a class, use new T() instead.
DO NOT Use this pattern on mutable reference types. Use them on immutable reference types like System.String.
DO NOT Use this pattern on types where value semantics don't make sense.

3 Comments

  1. Dan Puzey

    (Reposting a shorter version of an earlier comment since I can’t tell whether my first attempt submitted or not… I got no feedback when hitting “submit.”)

    Hi David,

    I wanted to draw your attention to a flaw in your pattern. It’s important that you only use this pattern for immutable types (such as most structs, and some classes like String). With an immutable type, if you make an assignment, you create a copy of the type. With a non-immutable type you are referring to the same instance.

    This is important. With your struct version of Point, if you say “a = Point.Empty” then you have created a copy of Point.Empty. When Point is a class (as with your final sample), this is not the case. Consider this code:

    var a = Point.Empty;
    a.X = 23;

    If Point is a class, then the above code results in Point.Empty being (23,0). This could be catastrophic to an application relying on this behaviour! More directly, if Point is a class as per your final example, I can simply perform “Point.Empty.X = 23” and it will modify the static instance. (If Point is a struct then the framework will prevent me from modifying fields of a readonly instance).

    I hope this is useful – perhaps your “do’s and do not’s” could be updated to be more correct.

    1. David Anderson (Post author)

      I did leave much to be discussed in terms of reference types, and I completely agree with you. Using this pattern on a mutable reference type would have the wrong behavior. I will update the DO’s and DO NOT’s and later will add that point to the article. Thanks for the feedback Dan.

  2. Dan Puzey

    Hi David. There’s a flaw in your explanation here, and it’s an important one. You can only use .Empty in this way for *value* types.

    The reason it works for value types is that an assignment is a copy operation. When you have a struct and you call Point p = Point.Empty, you’re creating a copy of that struct. However, when you do this with a class – a reference type – you create a copy *of the reference*, but the instance you reference isn’t copied.

    This is hugely important. Consider the following code snippet:

    var x = Point.Empty;
    x.X = 23;

    If your “Point” is a struct then this will behave as you described. However, if your Point is a class, then Point.Empty is now (23, 0). In fact, if your point is a class then *even though Empty is ReadOnly* I can call “Point.Empty.X = 23” directly with no error.

    The real distinction is that you should only use this pattern on an *immutable* type, such as String. String is a class but, if you change it, then you create a new instance of it. For this reason, String.Empty functions as expected. Most structs are immutable (though this is not a requirement – more a widely-maintained convention), and so this pattern is typically seen on structs types and not classes.

Leave a Comment