This morning I saw a great post on the C# Subreddit – “What is the difference of using auto-properties over properties in C#?” At the start of my career, I was confused about when to use each, and why properties were any better than public fields.

When you have a property, you are providing a way to get or set values on an object. Generally properties will be simple, such as names. Sometimes you may want validations on your properties, or for a property set to have a side-effect. This is common in WPF for raising the PropertyChange event on an INotifyPropertyChange implementor.

Later in the thread, someone asked about the difference between the following two sets of code.

public string Name;
public string Name { get; set; }

Functionally they do the same thing, don’t they? Well… not quite.

Warning: incoming technical details. For a guided tour, watch my video on this topic!

When you have a public field, you access the field directly.

When you have a public property, you access the backing field through a method. The get/set hides that code from you.

Check out this example.

        class Program
        {
            static void Main(string[] args)
            {
                var p1 = new Person_Property();
                p1.Name = "Elias";

                Console.WriteLine(p1.Name);

                var p2 = new Person_PublicField();
                p2.Name = "CodeKnightmare";

                Console.WriteLine(p2.Name);
            }
        }

        public class Person_Property
        {
            public string Name { get; set; }
        }

        public class Person_PublicField
        {
            public string Name;
        }

These two classes look very similar – Person_Property and Person_PublicField have a Name.

When we compile this, let’s look at the Intermediate Language (IL) that .NET generates to access the Name.

    private static void Main(/*Parameter with token 08000001*/string[] args)
    // .maxstack 2
    // .locals init (
    // [0] class ConsoleApp5.Person_Property 'personProperty [Range(Instruction(IL_0006 stloc.0)-Instruction(IL_0013 ldloc.0))]',
      // [1] class ConsoleApp5.Person_PublicField 'personPublicField [Range(Instruction(IL_0024 stloc.1)-Instruction(IL_0030 ldloc.1))]'
    // )
    // 
    // IL_0000: nop
    //
    // Right here we create our p1 object of type Person_Property.
    //
    // IL_0001: newobj       instance void ConsoleApp5.Person_Property::.ctor()
    // IL_0006: stloc.0      // 'personProperty [Range(Instruction(IL_0006 stloc.0)-Instruction(IL_0013 ldloc.0))]'
    // IL_0007: ldloc.0      // 'personProperty [Range(Instruction(IL_0006 stloc.0)-Instruction(IL_0013 ldloc.0))]'
    //
    //    Here's where we store the string constant "Elias" into the
    //  name property. 
    //
    //  Notice we're calling a method!
    //
    // IL_0008: ldstr        "Elias"
    // IL_000d: callvirt     instance void ConsoleApp5.Person_Property::set_Name(string)
    // IL_0012: nop
    //
    //  Here's where we write the Name property out to the
    //  console. Notice it's fetched using a method!
    //
    // IL_0013: ldloc.0      // 'personProperty [Range(Instruction(IL_0006 stloc.0)-Instruction(IL_0013 ldloc.0))]'
    // IL_0014: callvirt     instance string ConsoleApp5.Person_Property::get_Name()
    // IL_0019: call         void [System.Console]System.Console::WriteLine(string)
    // IL_001e: nop
    //
    //  Here's where we have the field version of Person.
    //
    // IL_001f: newobj       instance void ConsoleApp5.Person_PublicField::.ctor()
    // IL_0024: stloc.1      // 'personPublicField [Range(Instruction(IL_0024 stloc.1)-Instruction(IL_0030 ldloc.1))]'
    // IL_0025: ldloc.1      // 'personPublicField [Range(Instruction(IL_0024 stloc.1)-Instruction(IL_0030 ldloc.1))]'
    //
    //  Here's where we store the string constant "CodeKnightmare" 
    //  into Person_PublicField's Name field. Notice how we're 
    //  using stfld instead of call to set the value!
    //
    // IL_0026: ldstr        "CodeKnightmare"
    // IL_002b: stfld        string ConsoleApp5.Person_PublicField::Name
    // IL_0030: ldloc.1      // 'personPublicField [Range(Instruction(IL_0024 stloc.1)-Instruction(IL_0030 ldloc.1))]'
    // IL_0031: ldfld        string ConsoleApp5.Person_PublicField::Name
    // IL_0036: call         void [System.Console]System.Console::WriteLine(string)
    // IL_003b: nop
    // IL_003c: ret
    // 
    {
      Person_Property personProperty = new Person_Property();
      personProperty.Name = "Elias";
      Console.WriteLine(personProperty.Name);
      Person_PublicField personPublicField = new Person_PublicField();
      personPublicField.Name = "CodeKnightmare";
      Console.WriteLine(personPublicField.Name);
    }

You might wonder, what do these classes look like then?

Here’s the IL for Person_PublicField.

public class Person_PublicField
{
/Field Name with token 04000002/
// .field public string Name
public string Name;
/*Method .ctor with token 06000006*/
// .method public hidebysig specialname rtspecialname instance void
// .ctor() cil managed
public Person_PublicField()
// .maxstack 8
// 
// IL_0000: ldarg.0      // this
// IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
// IL_0006: nop
// IL_0007: ret
// 
{
  base.\u002Ector();
}
}

And here’s the IL for Person_Property. Notice how the compiler generates a get & set method to wrap the field, and notice how the compiler has generated a backing field for our property!

public class Person_Property
{
/Field k__BackingField with token 04000001/
// .field private string 'k__BackingField'
[/Attribute with token 0C000001/CompilerGenerated]
[/Attribute with token 0C000002/DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string \u003CName\u003Ek__BackingField;
/*Property Name with token 17000001*/
// .property instance string Name()
public string Name
{
  /*Method get_Name with token 06000003*/
  // .method public hidebysig specialname instance string
    // get_Name() cil managed
  [/*Attribute with token 0C00000D*/CompilerGenerated] get
  // .maxstack 8
  // 
  // IL_0000: ldarg.0      // this
  // IL_0001: ldfld        string ConsoleApp5.Person_Property::'<Name>k__BackingField'
  // IL_0006: ret
  // 
  {
    return this.\u003CName\u003Ek__BackingField;
  }
  /*Method set_Name with token 06000004*/
  // .method public hidebysig specialname instance void
    // set_Name(
      // string 'value'
    // ) cil managed
  [/*Attribute with token 0C00000E*/CompilerGenerated] set
  // .maxstack 8
  // 
  // IL_0000: ldarg.0      // this
  // IL_0001: ldarg.1      // 'value'
  // IL_0002: stfld        string ConsoleApp5.Person_Property::'<Name>k__BackingField'
  // IL_0007: ret
  // 
  {
    this.\u003CName\u003Ek__BackingField = value;
  }
}

/*Method .ctor with token 06000005*/
// .method public hidebysig specialname rtspecialname instance void
  // .ctor() cil managed
public Person_Property()
// .maxstack 8
// 
// IL_0000: ldarg.0      // this
// IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
// IL_0006: nop
// IL_0007: ret
// 
{
  base.\u002Ector();
}
}

Published by Elias

Elias Puurunen is a versatile entrepreneur and President of Northern HCI Solutions Inc., an IT consulting firm which has worked with Fortune 500 companies, governments, and startups. He has spoken at conferences in Canada and the United States and has been published around the world. Part of his work led to an agreement between the Canadian Government and Siemens Canada, creating jobs and investment into green infrastructure. His company's event management app, the Tractus Event Passport connects people at conferences, seminars and symposiums across Canada. Today he is a consultant and advisor to technology firms and government organizations. He lectures at the University of Waterloo on Coding for Policy Analysis for the School of Public Policy. He is the author of Beyond Passwords: Secure Your Business, a cyber-security book for small business owners.

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: