Sunday, February 15, 2009

C# 3.0 & LINQ : Auto-implemented Properties

As my quest to understand the finer details of LINQ which I would present to my team at Imageright, I thought it would be a great idea to start from scratch and look at each functionality of C# language which enables LINQ. In that process, I start with Auto-implemented Properties.

Defining auto-implemented property

   1: public sealed class Person{


   3:        public string FirstName { get; set; }


   5: }

The above class Person, defines an auto-implemented properties. you see can see both setter and getter defined. We will dig more into these in a while before which we should look at the compiler generated code. I used Reflector to decompile the source and this is what it looks like.

   1: public sealed class Person

   2: {

   3:     // Fields

   4:     [CompilerGenerated]

   5:     private string <FirstName>k__BackingField;


   7:     // Properties

   8:     public string FirstName

   9:     {

  10:         [CompilerGenerated]

  11:         get

  12:         {

  13:             return this.<FirstName>k__BackingField;

  14:         }

  15:         [CompilerGenerated]

  16:         set

  17:         {

  18:             this.<FirstName>k__BackingField = value;

  19:         }

  20:     }

  21: }


Clearly, nothing exceptionally new has been added to the IL and the compiler just generates C# 2.0 like properties from the above property – thus aptly named auto-implemented properties. Notice that a hidden backing field is used to support the property.

CompilerGeneratedAttribute[System.Runtime.CompilerServices] is applied to any element that is not user written and is automatically generated by the compiler. This attribute can be used to determine if an element is generated by the compiler.

Read-Only Properties

   1: //read-only property

   2: public readonly string LastName { get; set; }

The above shown way is INCORRECT. The compiler does not allow readonly keyword on the auto-prop. The way you achieve read-only is as shown below.

   1: //read-only property

   2: public string PersonCode { get; private set; }

The compiler generated code seems to add “private’ before the setter, just like the way we did.

   1: public string PersonCode

   2:   {

   3:       [CompilerGenerated]

   4:       get

   5:       {

   6:           return this.<PersonCode>k__BackingField;

   7:       }

   8:       private [CompilerGenerated]

   9:       set

  10:       {

  11:           this.<PersonCode>k__BackingField = value;

  12:       }

  13:   }

As a side-note, auto-implemented properties can also be static.

   1: //statics are allowed

   2: public static string ClassName{

   3:             get;

   4:             set;

   5: }

Write-Only Properties

Well, this might not make sense to use (in practical scenarios), but write-only properties are also allowed. When one tries to read a write-only property, the compiler generates the following error.

Error    1    The property or indexer 'LinqBasics.Person.SecretCode' cannot be used in this context because the get accessor is inaccessible

Shown below is a write-only property.

   1: public string SecretCode { private get; set; }

Also note that, you can re-order the get and set [does not matter, though].

   1: public string Ext { private set; get; }

   2: //order of set and get does not matter

What are not allowed?

The accessibility modifier for the accessor should be more restrictive than the property or the indexer.

The following property is invalid.

   1: private string ExtensionWide { private get; set; }

   2: //private get is not more restrictive than private applied on the property

   3: //thus it is not a valid declaration

Similarly, a private property cannot have a public accessor.

You cannot specify accessibility modifiers for both accessors of a property or indexer.

The following is invalid.

   1: public string InvalidProperty { private get; private set; }

   2: //modifiers on both accessors not allowed.

Indexers cannot be defined using auto-implemented properties.

If you do so, you end up with compiler error

   1: //indexer property cannot be defined using short-hand notation

   2: public string this[int index]

   3: {

   4:             get; 

   5:             set;

   6: }

   7: /*Errors you would notice

   8: 'LinqBasics.Person.this[int].get' must declare a body because it is not marked 

   9: abstract, extern, or partial

  10: */

Automatically implemented properties must defined both get and set accessors.

The following is not valid.

   1: public string LastNameX { get; } 

   2: //missing set accessor, thus invalid.

Normal properties can be initialized, while auto-implemented properties cannot be initialized.

See the code below.

   1: //Normal properties can be initialized.

   2: private string _lastName = "Vangapandu";

   3: public string LastName{ get { return _lastName; }}


   5: //Auto-implemented properties cannot be initialized.

   6: public string ZipCode { get; set; } = "30605";

The documentation also talks about creating immutable classes using auto-implemented properties. So, now lets make the Person class immutable (we make all the properties as read-only and privatize the constructor. also we provide a factory method for instantiation.)

   1: public sealed class Person

   2:     {

   3:         public string FirstName { get; private set; }

   4:         public string LastName { get; private set; }


   6:         private Person()

   7:         {

   8:         }


  10:         public static Person CreatePerson(string firstname, string lastname)

  11:         {

  12:             Person p = new Person();

  13:             p.FirstName = firstname;

  14:             p.LastName = lastname;

  15:             return p;

  16:         }

  17:     }

I will add more information later if required. I hope this article is as informative as I wanted it to be.

1 comment:

Dave said...

Great post! this helped me because along with my auto property being in a singleton, I needed to make sure that a certain auto property can't be set also. I wanted all the logic to be completely contained and managed within my singleton object.