Automatic Property Declaration - Expert C# 5.0: with .NET 4.5 Framework (2013)

Expert C# 5.0: with .NET 4.5 Framework (2013)

CHAPTER 5

images

Automatic Property Declaration

This chapter will discuss automatic property declaration, which is a simplified syntax to declare a property for a class. I will also discuss the implicit variable declaration using the var keyword and show how var is handled by the C# compiler in design and runtime. Finally, I will discuss the anonymous type declaration and how the C# compiler compiles it.

Automatic Property

You can use the class of a real-world object to encapsulate its characteristics. For example, if you think about a real-life object, such as a book of class Book as demonstrated in Listing 5-1, you can see it has a name, a publication year, and the author field to define it.

Listing 5-1. A Class of the Book

namespace Ch05
{
public class Book
{
private string name; /* name field to define Book*/
private int publishedYear; /* publishedYear field to define Book*/
private string author; /* author field to define Book*/

public Book()
{
name = default(string);
publishedYear = default(int);
author = default(string);
}

public Book(string nameOfTheBook, int publishedYearOfTheBook, string authorOfTheBook)
{
name = nameOfTheBook;
publishedYear = publishedYearOfTheBook;
author = authorOfTheBook;
}

/* A method to get the value of name field */
public string GetName() { return name; }

/* A method to set the value of name field */
public void SetName(string nameOfTheBook) { name = nameOfTheBook; }

/* A method to get the value of publishedYear field */
public int GetPublishedYear() { return publishedYear; }

/* A method to set the value of publishedYear field */
public void SetPublishedYear(int publishedYearOfTheBook)
{ publishedYear = publishedYearOfTheBook; }

/* A method to get the value of author field */
public string GetAuthor() { return author; }

/* A method to set the value of author field */
public void SetAuthor(string authorOfTheBook) { author = authorOfTheBook; }
}
}

The Book class from Listing 5-1 has three fields, and these are accessible to the outside of the Book class via get and set accessors. Using the get and set methods, you can expose private fields to the consumer of the Book class. You can make the field read only, write only, or read-write only by using only the get method or the set method or get-set both to make it read writable. In .NET, you can also use the concept called Property to encapsulate private fields and to replace the get and set methods used in Listing 5-1, as shown in Listing 5-2.

Listing 5-2. The Book Class with Property

namespace Ch05
{
public class Book
{
private string name;
private int publishedYear;
private string author;

public Book()
{
name = default(string);
publishedYear = default(int);
author = default(string);
}

public Book(string nameOfTheBook, int publishedYearOfTheBook, string authorOfTheBook)
{
name = nameOfTheBook;
publishedYear = publishedYearOfTheBook;
author = authorOfTheBook;
}
public string Name /* Name property */
{
get { return name; }
set { name = value; }
}

public int PublishedYear /* PublishedYear property */
{
get { return publishedYear; }
set { publishedYear = value; }
}

public string Author /* Author property */
{
get { return author; }
set { author = value; }
}
}
}

The Book class in Listing 5-2 defined the properties Name, PublishedYear, and Author for the private fields name, publishedYear, and author. These properties will eliminate the need to define the get and set methods manually in the type; behind the scenes the C# compiler takes care of that when you use a property to expose the private fields. When the C# compiler compiles the Book class, as defined in Listing 5-2, it implements the get and set method automatically to expose private fields used for the properties, for example, the name field for the Name property. This is a wrapper for the get and set methods to expose the fields from the type.

Let’s decompile the Book class as shown in Listing 5-2 into IL to find out more about the Property definition defined by the C# compiler, as shown in Listing 5-3.

Listing 5-3. ILCode of the Book Class.

.class public auto ansi beforefieldinit Book extends [mscorlib]System.Object
{
/* Code removed */
.property instance string Author
{
.get instance string
Ch05.Book::get_Author() /* C# Compiler generates the get_Author method */
.set instance void
Ch05.Book::set_Author(string) /* C# Compiler generates the set_Author method */
}

.property instance string Name
{
.get instance string
Ch05.Book::get_Name() /* C# Compiler generates the get_Name method */
.set instance void
Ch05.Book::set_Name(string) /* C# Compiler generates the set_Name method */
}

.property instance int32 PublishedYear
{
.get instance int32
Ch05.Book::get_PublishedYear() /* C# Compiler generates the get_PublishedYear
* method */
.set instance void
Ch05.Book::set_PublishedYear(int32) /* C# Compiler generates the set_PublishedYear
* method */

}
.field private string author /* Private field*/
.field private string name /* Private field*/
.field private int32 publishedYear /* Private field*/
}

In Listing 5-3, you can see that the C# compiler generates the get and set method for the private fields, for example, for the Author property it implements two new methods such as get_Author and set_Author to encapsulate the author field. This is also done for the Name and PublishedYear property, for which the C# compiler implements get_Name and set_Name and get_PublishedYear and set_PublishedYear.

If you examine the get_Author and set_Author method in IL implementation, as shown in Listing 5-4, you will understand how a C# compiler implements these get and set methods.

Listing 5-4. Implementation of the get_Author() and set_Author(string)

.method public hidebysig specialname instance string get_Author() cil managed
{
.maxstack 1
.locals init (
[0] string CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldfld string Ch05.Book::author /* It loads the value of the private field
* author */

L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}

.method public hidebysig specialname instance void set_Author(string 'value') cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldarg.0
L_0002: ldarg.1
L_0003: stfld string Ch05.Book::author /* It replace the value of the field author
* with the given value */

L_0008: ret
}

In Listing 5-4, the get_Author method loads the value of the author field and returns to the caller of the Author property as output. The set_Author method loads the argument value at position 1 from the argument array of the Method state description table defined for the set_Author (by the C# compiler) onto the evaluation stack using the instruction ldarg.1 in L_0002. The value of the author field of the Book class is stored using the stfld instruction in L_0003.

· ldfld field - Push the value of field of object (or value type) aObject, onto the stack

· stfld field - Replace the value of field of the object aObject with value.

The property declaration in C# reduces the task of defining get and set methods in a class. The property declaration concept has been abstracted a step further to make it easier by introducing the Automatic property declaration concept. In this concept you do not need to define any private fields for the class, just declare the property for the class with the get; and set; statements in it. The CLR will take care of the rest (i.e., it will define private fields for the relevant property as well as define the get and set methods for which CLR-generated private fields to expose via the relevant property).

In Listing 5-5, the Book class defines three automatic properties: Name, PublishedYear, and Author.

Listing 5-5. Automatic Property in C#

namespace Ch05
{
public class Book
{
/* Assigning default value to the Property */
public Book()
{
Name = default(string);
PublishedYear = default(int);
Author = default(string);
}

/* Assigning value to the Property */
public Book(string nameOfTheBook, int publishedYearOfTheBook, string authorOfTheBook)
{
Name = nameOfTheBook;
PublishedYear = publishedYearOfTheBook;
Author = authorOfTheBook;
}

/* Automatic property declaration for the Name */
public string Name {get; set;}

/* Automatic property declaration for the PublishedYear */
public int PublishedYear { get; set; }

/* Automatic property declaration for the Author */
public string Author{get; set; }
}
}

The Book class in Listing 5-5 has not declared any private fields for the Name, PublishedYear, and Author properties, and these properties do not have the implementation code inside the get and set accessors (i.e., it has not explicitly mentioned which field to expose, and this is the beauty of the automatic property concept). When you declare any automatic property in a type, the C# compiler:

· Adds a private field for that property, such as for Name, Author, and PublishedYear, and the CLR adds the private fields <Name>k__BackingField, <Author>k__BackingField, and <PublishedYear>k__BackingField.

· Implements the get and set method for the private field to get and set values from that property, such as get_Name and set_Name for the Name property and so on.

Let’s decompile the Book class defined in Listing 5-5 into IL code to find out how the C# compiler adds private fields and get and set methods for the automatic property, as shown in Listing 5-6.

Listing 5-6. IL Code for the Automatic Property

.class public auto ansi beforefieldinit Book extends [mscorlib]System.Object
{
/* Code removed */
.property instance string Author
{
/* C# Compiler generates the get_Author method */
.get instance string Ch05.Book::get_Author()
/* C# Compiler generates the set_Author method */
.set instance void Ch05.Book::set_Author(string)
}
.property instance string Name
{
/* C# Compiler generates the get_Name method */
.get instance string Ch05.Book::get_Name()
/* C# Compiler generates the set_Name method */
.set instance void Ch05.Book::set_Name(string)
}
.property instance int32 PublishedYear
{
.get instance int32
Ch05.Book::get_PublishedYear() /* C# Compiler generates the get_PublishedYear
* method */

.set instance void
Ch05.Book::set_PublishedYear(int32) /* C# Compiler generates the set_PublishedYear
* method */
}
.field private string <Author>k__BackingField { /* Code removed*/ }
.field private string <Name>k__BackingField { /* Code removed*/ }
.field private int32 <PublishedYear>k__BackingField { /* Code removed*/ }
}

The CLR adds three private fields: <Author>k__BackingField, <Name>k__BackingField, and <PublishedYear>k__BackingField for the Author, Name, and PublishedYear properties, respectively, in the Book class. It implements the get and set methods for those private fields, such as, for the Author property the CLR addsget_Author and set_Author method and likewise for the Name and PublishedYear property.

Let’s examine the get_Author and set_Author, get_Name and set_Name, get_PublishedYear and set_ PublishedYear method, as implemented in Listing 5-7, from the generated IL code in Listing 5-5.

Listing 5-7. IL Code for the get and set Methods

/* To read value from the Author property about the author */
.method public hidebysig specialname instance string get_Author() cil managed
{
/* Code removed */
L_0001: ldfld string Ch05.Book::<Author>k__BackingField /* It loads the value of the field
* <Author>k__BackingField */
/* Code removed */
}

/* To write value about the author via the Author property. The Author property is read-
writeable */
.method public hidebysig specialname instance void set_Author(string 'value') cil managed
{
/* Code removed */
L_0002: stfld string Ch05.Book::<Author>k__BackingField /* It replaces the value of the
* field <Author>k__BackingField
* with the given value */
/* Code removed */
}

/* To read value from the Name property about the name */
.method public hidebysig specialname instance string get_Name() cil managed
{
/* Code removed */
L_0001: ldfld string Ch05.Book::<Name>k__BackingField /* It loads the value of the field
* <Name>k__BackingField */
/* Code removed */
}

/* To write value about the name via the Name property. The Name property is read-writeable */
.method public hidebysig specialname instance void set_Name(string 'value') cil managed
{
/* Code removed */
L_0002: stfld string Ch05.Book::<Name>k__BackingField /* It replaces the value of the
* field <Name>k__BackingField
* with the given value */
/* Code removed */
}

/* To read value from the PublishedYear property about the published year */
.method public hidebysig specialname instance int32 get_PublishedYear() cil managed
{
/* Code removed */
L_0001: ldfld int32 Ch05.Book::<PublishedYear>k__BackingField /* It loads the value of the
* field <PublishedYear>k__
* BackingField */
/* Code removed */
}

/* To write value about the published year via the PublishedYear property. The PublishedYear
* property is read-writeable */
.method public hidebysig specialname instance void set_PublishedYear(int32 'value') cil managed
{
/* Code removed */
/* It replaces the value of the field <PublishedYear>k__BackingField with the given value */
L_0002: stfld int32 Ch05.Book::<PublishedYear>k__BackingField
/* Code removed */
}

images

Figure 5-1. Variable declaration in C#

var Implicitly Typed Local Variable

In C#, you can declare a variable using a type name and then the variable name followed by ;, as shown in Figure 5-1. The C# compiler will then know what type of variable it is and what the name of the variable is.

To declare a variable you need a type name, which will tell the CLR to allocate appropriate memory based on the type name, assign that memory an address, and associate it with the variable name. This is an explicit variable declaration in .NET, which you can define by using the keywordvar in your program to define a variable. An example of the usage of the var keyword is shown in Listing 5-8.

Listing 5-8. Implicit Type Declaration Using var Keyword

using System;

namespace Ch05
{
class Program
{
static void Main(string[] args)
{
var person = new Person /* type of Person */
{
Name = "A Person"
};
var personAge = 30; /* type of int */
var authorOf = "Expert C# 5.0: with the .NET 4.5 Framework"; /* type of string */

Console.WriteLine("Name:{0}\nAge: {1}\nBook: {2}",
person.Name, personAge, authorOf);
}
}

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

This program will produce the following output:

Name: A Person
Age: 30
Book: Expert C# 5.0: with the .NET 4.5 Framework

In Listing 5-8, Program class defines three variables—person, personAge, and authorOf—using the var keyword. Let’s explore how the CLR deals with the var keyword.

var in Runtime

The C# compiler infers the type of the variable from the right-hand-side expression. For example, the type for the person is inferred from the type of the right-hand-side expression new Person{….}, which makes person a type of Person in the same way as for personAge as int and authorOf as string.

Let’s explore more about this by examining the decompiled IL code for Listing 5-8, as shown in Listing 5-9.

Listing 5-9. IL Code of the C# code in Listing 5-8

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 4
.locals init (
[0] class Ch05.Person person,
[1] int32 personAge,
[2] string authorOf,
[3] class Ch05.Person <>g__initLocal0)
L_0000: nop

/* newobj instantiates an instance of the Person type onto the Heap.*/
L_0001: newobj instance void Ch05.Person::.ctor()

/* Stores the heap address reference of the Person object instantiated in L_0001 into the
* Locals section of
* the stack at position 3.*/
L_0006: stloc.3

/* Loads the Person object <>g__initLocal0 */
L_0007: ldloc.3
L_0008: ldstr "A Person"

/* Sets the value for the Name property of the Person object*/
L_000d: callvirt instance void Ch05.Person::set_Name(string)
L_0012: nop

/* Loads the Person object <>g__initLocal0 */
L_0013: ldloc.3

/* The Person object (<>g__initLocal0) at the position 3 (Load in L_0013) will be stored
* into the Locals section of the Main method stack at position 0.*/
L_0014: stloc.0
L_0015: ldc.i4.s 30
L_0017: stloc.1
L_0018: ldstr "Expert C# 5.0: with the .NET 4.5 Framework"
L_001d: stloc.2
L_001e: ldstr "Name:{0}\nAge:{1}\nBook: {2}"
L_0023: ldloc.0

/* get the Name property value of the Person object*/
L_0024: callvirt instance string Ch05.Person::get_Name()
L_0029: ldloc.1
L_002a: box int32
L_002f: ldloc.2
L_0030: call void [mscorlib]System.Console::WriteLine(string, object, object, object)
L_0035: nop
L_0036: ret
}

The C# compiler sets the type for the person object as Ch05.Person, personAge as int32, and authorOf as string. In Listing 5-9, there is another variable, <>g__initLocal0 type of Ch05.Person, that has been defined. In L_0001 the newobj instruction instantiates an instance of Person type and stores it in the local variable at position 3 (<>g__initLocal0) and sets the value for the Name property in L_000d. In L_0013, the <>g__initLocal0 object will be stored in the local variable at position 0 (person). The C# compiler sets the instruction to load the value 30 (0x1E) into the local variable at position 1 of the Local section of the Main method for personAge. Finally, it sets the instruction to load the string literally "Expert C# 5.0: with the .NET 4.5 Framework" into the local variable authorOf at position 2 of the Local section of the Main method.

In C#, the variables declared with var keyword are strongly typed and the compiler makes sure to associate those with the appropriate type before they get executed. The approximate C# code shown in Listing 5-10 is for the Main method of Listing 5-8 after compilation,

Listing 5-10. Compiler Modified Code for Listing 5-8

private static void Main(string[] args)
{
Person <>g__initLocal0 = new Person { /* change var to Person */
Name = "A Person"
};
Person person = <>g__initLocal0;
int personAge = 30; /* change var to int */
string authorOf = "Expert C# 5.0: with the .NET 4.5 Framework"; /* change var to string */

Console.WriteLine("Name:{0}\nAge:{1}\nBook: {2}", person.Name, personAge, authorOf);
}

images

Figure 5-2. Var declared variable in the Stack and Heap

In Listing 5-10, you can see that the C# compiler sets the appropriate types for the relevant statement where the variable is declared using the var keyword. In the runtime, CLR will take care of all the variables declared with var the same as normal in respect to the stack and heap. Let’s explore more about this by examining the memory (stack and heap) status when CLR deals with using the var keyword in the Program class, as shown in Listing 5-8.

Figure 5-2 shows that the LOCALS section of the Main method’s Method state description table contains a Heap reference of the Person instantiated on the Heap at 0x0175b9c0, an object of a string instantiated on the Heap at 0x0175b93c and int value 0x1e (30).

images

Figure 5-3. Implicit variable declaration in design time

var in Design Time

In the design time, the C# compiler sets the appropriate type for those var declared variables. The variable declared using var keyword will be a strongly typed variable, and the C# compiler sets the type in design time as well. Figure 5-3 shows how the compiler sets the type for the variable declared using the keyword var.

Anonymous Type

The anonymous type is a way to declare a type that contains the property for the type, without any functionality in it. You can use anonymous type where you need to declare a type with only properties. There are several things you can do using anonymous type:

· It can define only properties for the type.

· The type of the property does not need to be declared, as it infers from the assigned value at runtime.

· You cannot define any method inside the anonymous type.

· Once the type is defined with the value, it is not possible to change the value of the properties.

Listing 5-11 shows an anonymous type anObjectOfAnonymousType that has been defined, which contains four properties, such as Name, Language, PublishedOn, and Description, with the value assign to it in declaration time.

Listing 5-11. An Anonymous Type Declaration

using System;

namespace Ch05
{
class Program
{
static void Main(string[] args)
{
/* Anonymous type definition*/
var anObjectOfAnonymousType = new
{
/* a string type */
Name = "Expert C# 5.0: with the .NET 4.5 Framework",
/* a string type */
Language = "C#",
/* a int type */
PublishedOn = 2012,
/* a DescriptionAboutBook type */
Description = new DescriptionAboutBook("This book is about C#")
};

Console.WriteLine("{0}\n{1}\n{2}\n{3}",
anObjectOfAnonymousType.Name,
anObjectOfAnonymousType.Language,
anObjectOfAnonymousType.PublishedOn,
anObjectOfAnonymousType.Description.Description);
}
}
public class DescriptionAboutBook
{
public DescriptionAboutBook(string data)
{
Description = data;
}
public string Description { get; set; }
}
}

This program produces the following output:

Expert C# 5.0: with the .NET 4.5 Framework
C#
2012
This book is about C#

This anObjectOfAnonymousType object will hold an object of an anonymous type, which has the properties and their associated values as shown in Table 5-1.

images

When the C# compiler finds the code shown in Listing 5-11, it will do the following:

· Define new type, which encapsulates the Name, Language, PublishedOn, and Description field inside it.

· Create an instance of that type and store it into the variable anObjectOfAnonymousType.

· The rest of the code in the Main method will then be able to access the values stored in this anonymous type field through this anObjectOfAnonymousType object.

The <>f__AnonymousType0 accepts four generic types:

<Name>j__TPar
<Language>j__TPar
<PublishedOn>j__TPar
<Description>j__TPar>

The <>f__AnonymousType0 class also defines four fields based on the above types, as shown in Table 5-2.

images

This <>f__AnonymousType0 class has a constructor that takes four parameters, such as Name, Language, PublishedOn, and Description of type <Name>j__TPar, <Language>j__TPar, <PublishedOn>j__TPar, and <Description>j__TPar>, which will be used to initialize the <Name>i__Field, <Language>i__Field, <PublishedOn>i__Field, and<Description>i__Field fields of the <>f__AnonymousType0 class.

The actual type for these <Name>j__TPar, <Language>j__TPar, <PublishedOn>j__TPar, and <Description>j__TPar> will be provided by the consumer of this <>f__AnonymousType0 anonymous type. The consumer of these anonymous types can only read values of the fields from the instance of this type because the property of this anonymous type has been defined as read only (i.e., only the get methods defined for the fields). Listing 5-12 shows the decompiled IL for the anonymous type defined in Listing 5-11.

Listing 5-12. Decompiled Code for the <>f__AnonymousType0 Type

.class private auto ansi sealed beforefieldinit <>f__AnonymousType0<
<Name>j__TPar, /* Generic type for the Name*/
<Language>j__TPar, /* Generic type for the Language*/
<PublishedOn>j__TPar, /* Generic type for the PublishedOn*/
<Description>j__TPar> /* Generic type for the Description*/
extends [mscorlib]System.Object
{
/* Constructor */
.method public hidebysig specialname rtspecialname instance void .ctor
(!<Name>j__TPar Name, /* Name type of !<Name>j__TPar */
!<Language>j__TPar Language, /* Language type of !<Language>j__TPar */
!<PublishedOn>j__TPar PublishedOn, /* PublishedOn type of !<PublishedOn>j__TPar */
!<Description>j__TPar Description) /* Description type of !<Description>j__TPar */
cil managed { /* Code removed*/ }

.property instance !<Description>j__TPar Description
{ .get instance !<Description>j__TPar <>f__AnonymousType0'4::get_Description() }

.property instance !<Language>j__TPar Language
{ .get instance !<Language>j__TPar <>f__AnonymousType0'4::get_Language() }

.property instance !<Name>j__TPar Name
{ .get instance !<Name>j__TPar <>f__AnonymousType0'4::get_Name() }

.property instance !<PublishedOn>j__TPar PublishedOn
{ .get instance !<PublishedOn>j__TPar <>f__AnonymousType0'4::get_PublishedOn() }

.field private initonly !<Description>j__TPar Description>i__Field { /* Code removed*/ }
.field private initonly !<Language>j__TPar <Language>i__Field { /* Code removed*/ }
.field private initonly !<Name>j__TPar <Name>i__Field { /* Code removed*/ }
.field private initonly !<PublishedOn>j__TPar PublishedOn>i__Field { /* Code removed*/ }
}

The <>f__AnonymousType0 class defines the get method to access the fields of this anonymous type, but this class does not have any set method. As a result, the value of the field or property cannot be changed (i.e., read only). The caller of this anonymous type, which is the Main method of theProgram class, shown in Listing 5-13, calls the newobj IL instruction in L_001a by passing the <>f__AnonymousType0 type name with the associated generic parameter type, for example, the string for <Name>j__TPar, string for <Language>j__TPar, int32 for the <PublishedOn>j__TPar, and Ch05.DescriptionAboutBook for the<Description>j__TPar>.

In the Main method of Listing 5-13, the instructions from L_0001 to L_0015 initialize the related data to make an instance of the <>f__AnonymousType0 type.

Listing 5-13. Decompiled IL Code for the Main Method from Listing 5-11

.class private auto ansi beforefieldinit Program extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
/* Code removed */
}
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 5
.locals init (
[0] class <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
anObjectOfAnonymousType,
[1] object[] CS$0$0000)

L_0000: nop
L_0001: ldstr "Expert C# 5.0: with the .NET 4.5 Framework"
L_0006: ldstr "C#"
L_000b: ldc.i4 0x7dc
L_0010: ldstr "This book is about C#"
L_0015: newobj instance void Ch05.DescriptionAboutBook::.ctor(string)

/* Instantiates an instance of the <>f__AnonymousType0'4 type*/
L_001a: newobj instance void <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
::.ctor(!0, !1, !2, !3)
L_001f: stloc.0
L_0020: ldstr "{0}\n{1}\n{2}\n{3}"
L_0025: ldc.i4.4
L_0026: newarr object
L_002b: stloc.1
L_002c: ldloc.1
L_002d: ldc.i4.0

/* Loads the instance of the <>f__AnonymousType0'4 type stored at position 0 of
* Locals section */

L_002e: ldloc.0

/* To read the field value from the instance of the <>f__AnonymousType0'4 type*/
L_002f: callvirt instance !0 <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
::get_Name()
L_0034: stelem.ref
L_0035: ldloc.1
L_0036: ldc.i4.1

/* Loads the instance of the <>f__AnonymousType0'4 type stored at position 0 of Locals
* section */

L_0037: ldloc.0

/* To read the field value from the instance of the <>f__AnonymousType0'4 type*/
L_0038: callvirt instance !1 <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
::get_Language()
L_003d: stelem.ref
L_003e: ldloc.1
L_003f: ldc.i4.2

/* Loads the instance of the <>f__AnonymousType0'4 type stored at position 0 of Locals
* section */

L_0040: ldloc.0

/* To read the field value from the instance of the <>f__AnonymousType0'4 type*/
L_0041: callvirt instance !2 <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
::get_PublishedOn()
L_0046: box int32
L_004b: stelem.ref
L_004c: ldloc.1
L_004d: ldc.i4.3

/* Loads the instance of the <>f__AnonymousType0'4 type stored at position 0 of Locals
section */
L_004e: ldloc.0

/* To read the field value from the instance of the <>f__AnonymousType0'4 type*/
L_004f: callvirt instance !3 <>f__AnonymousType0'4
<string, /* The Type for the <Name>j__TPar */
string, /* The Type for the <Language>j__TPar */
int32, /* The Type for the <PublishedOn>j__TPar */
class Ch05.DescriptionAboutBook> /* The Type for the <Description>j__TPar> */
::get_Description()
L_0054: callvirt instance string Ch05.DescriptionAboutBook::get_Description()
L_0059: stelem.ref
L_005a: ldloc.1
L_005b: call void [mscorlib]System.Console::WriteLine(string, object[])
L_0060: nop
L_0061: ret
}
}

The rest of the IL code will access this instance of the <>f__AnonymousType0 type and access the related property, for example:

· The instruction ldloc.0 in L_002e loads the local variable stored onto the position 0, which is anObjectOfAnonymousType, into the evaluation stack.

· On the following IL instruction, L_002f will call the method get_Name of the DescriptionAboutBook type to get the value of the Name field of the DescriptionAboutBook type.

Code from the L_0037 to L_0054 will be used to get the values of the different fields. Finally, this value will be displayed on the console as output.

As you saw earlier, the value of the anonymous type’s property is read only, and you can define the value of the different properties of the type while you define it but you cannot change the value of the property after the instantiation of the anonymous type. The property of the anonymous type only implements the get method, and as a result it will not be able to change the value after it is instantiated. The C# compiler shows an error message when you build Listing 5-11 by adding the line of code shown in Listing 5-14 to update the Name field from the anonymous type instantiated.

Listing 5-14. To Modify the Name Property of the Anonymous Type

anObjectOfAnonymousType.Name = "Try to add new name";

When you try to assign a new value of the Name property of the anonymous type, the C# compiler raises the following error:

Error 1 Property or indexer 'AnonymousType#1.Name' cannot be assigned to -- it is read
only

Summary

In this chapter we have explore how the C # compiler implements the automatic property behind the scenes. You also explored the implicitly typed local variable using the var keyword as well as the anonymous type. In the next chapter, you will learn about the enum in C#.