I’ve been excited and curious about the fancy auto-backed properties of C# since I first got into the language about three years ago. I’ll admit that, though I’m not a language nerd, the improved ergonomics made possible by these properties are great and I’d love to know how they work under the hood.

For those that haven’t seen them before, auto-backed properties are a nice way to set access levels for fields in a class. In traditionally object oriented language, you’d declare private fields, and go through the tedious process of manually write getters and setters.

Pre-C# 3, you would write something like:

public class FooClass {
  private string mMyString;

  public string getMyString() {
    return mMyString;
  }

  public void setMyString(string newString) {
    mMyString = newString;
  }
}

But with C# 3, you could now use the shorthand syntax:

public class FooClass {
  public string MyString { get; set; }
}

var f = new FooClass()

f.MyString = "Hello world";

This is really neat, makes sure you’re not using completely public fields, and it ensures source compatibility with code that depends on FooClass, if in the future you want to tighten the accessibility or validation on the field.

It’s always nice to take a look at the in-depth implementation of basic things you take for granted in a language like C#. So I decided to write this brief guide on the intricacies of auto-backed properties.

How does it work?

At the most basic level, auto-backed properties are just syntactic sugar for creating a field that has autogenerated getters and setters, all generated under the hood by the compiler. A intermediate compiled version might be:

public class FooClass {
  private string vMyString;
  public string getMyString() {
    return vMyString;
  }
  public setMyString(string myString) {
    this.vMyString = myString;
  }
}

var f = new FooClass();

f.setMyString("Hello world");

Note the field that’s backing the MyString property is auto-generated by the compiler, with an auto-generated name as well. You cannot access the raw underlying field from within your code.

Why does this matter? (or, “can I forget about writing getter and setter functions forever?”)

Well, there’s a few considerations you should make when writing such code, or converting old code.

These primarily come down to three factors:

  • Performance - if speed is of the essence and worth breaking from concise and easy-to-reason-about property syntax, then you might want to use public fields (or manually boilerplated getters and setters)

  • Source compatibility - you’ll need to recompile all dependent libraries and codebases, and maybe change how you access your data

  • Immutable fields (readonly modifier) in pre-C# 6 codebases - immutability is pretty handy and quite important when running concurrent operations, so this may matter to you

Performance

The boilerplate enforced by manual getters and setters may have made legacy projects rely heavily on public fields. If you’re looking at converting a bunch of old public fields (which break the OO principle of encapsulation), you may notice a difference in performance if your work is performance-critical, as outlined by Microsoft and many threads on Stack Overflow.

Separately, the use of auto-backed properties over privately declared fields and boilerplate getters + setters can have subtle performance consequences when it comes to serialisation of your class objects. This was actually something I found out last week. As the Nancy source documentation explains, this is purely an issue for serialising to XML elements.

Essentially, when generating auto-backed properties, the compiler selects names for the backing fields that are impossible to use in a C# identifier, including angle brackets (i.e. > and <). This is in order to prevent collisions with actual field names you may choose to use in your class.

If your serializer relies on reflecting fields to find data items in your class (which the built-in DataContractSerializer does), it will try and create an XML element with that field name.

Since XML elements aren’t allowed to have angle brackets in ther name (for obvious reasons), during serialisation you’ll run into exceptions (expensive) and the serialiser will have to escape the field names (also expensive).

Source compatibility

Again, the potential reliance on public fields means that all code that you move to auto backed properties that relies on your code must be recompiled against the new source code. This is an issue especially for library projects and components shared within an organisation. See the MSDN link for more.

Immutability in pre-C# 6 projects

As highlighted by this StackOverflow answer, and still true until fairly recently, immutable readonly fields still need to be manually interfaced by a getter field.

public class FooClass {
  private readonly string myString;

  public FooClass(string myString) {
    this.myString = myString;
  }

  public string getMyString() {
    return this.myString;
  }
}

This was impossible with C# 3’s auto getter/setter syntax as immutable properties through optional setters were not possible. However, with the advent of C# 6, you can simply implement a getter in the shorthand syntax:

public class FooClass {
  public myString { get; }

  public FooClass(string myString) {
    this.myString = myString;
  }
}

The compiler will automatically infer the immutability of this myString as well, and since it doesn’t expose the field directly to the FooClass in code, it can guarantee that myString won’t be modified within the class either.

Summary and further reading

It’s always nice to know what’s under the hood of some of your favourite language features, if just so you can be aware of when to and when to NOT use them.

If this level of implementation detail excites you, but you also don’t get that fascinated by machine code, I’d recommend reading more about .NET’s abstractions for things like parallelism and functional programming. They’re real fascinating.

For example, Eric Lippert’s excellent two-part post on LINQ translation rules and “transparent identifiers” gives you a bit of background on how LINQ works from a compiler perspective.

You could also check out Joe Duffy’s “15 Years of Concurrency”, which documents how concurrency research at Microsoft led to their task and futures API model.