In our ongoing example, we have the following getter method defined: -
class Address { protected string city; protected string zipCode; public string ZipCode { get { return zipCode; } } }
If you look at the resulting MSIL from this method, you'll see that the compiler has created an accessor method called get_ZipCode, as seen here: -
.method public hidebysig specialname instance string get_ZipCode() cil managed { // Code size 11 (0xb) .maxstack 1 .locals ([0] string _Vb_t_$00000003$00000000) IL_0000: ldarg.0 IL_0001: ldfld string Address::zipCode IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method Address::get_ZipCode
You can tell the name of the accessor method because the compiler prefixes the property name with get_ (for a getter method) or set_ (for a setter method). As a result, the following code resolves to a call to get_ZipCode: -
String str = addr.ZipCode; // this calls Address::get_ZipCode
Therefore, you might be tempted to try the following explicit call to the accessor method yourself: -
String str = addr.get_ZipCode; // **ERROR " Won't compile
However, in this case, the code will not compile because it's illegal to explicitly call an internal MSIL method.
The answer to our question-how can a compiler allow us to use the standard object.field syntax and have a method be called?-is that the compiler actually generates the appropriate getter and setter methods for us when it parses the C# property syntax. Therefore, in the case of the Address.ZipCode property, the compiler emits MSIL that contains the get_ZipCode and set_ZipCode methods.
Now let's look at the generated setter method. In the Address class you saw the following: -
public string ZipCode { set { // Validate value against some datastore. zipCode = value; // Update city based on validated zipCode. } }
Notice that nothing in this code declares a variable named value yet we're able to use this variable to store the caller's passed value and to set the protected zipCode member field. When the C# compiler generates the MSIL for a setter method, it injects this variable as an argument in a method called set_ZipCode.
In the generated MSIL, this method takes as an argument a string variable: -
.method public hidebysig specialname instance void set_ZipCode(string 'value') cil managed { // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string Address::zipCode IL_0007: ret } // end of method Address::set_ZipCode
Even though you can't see this method in the C# source code, when you set the ZipCode property with something like addr.ZipCode("12345"), it resolves to an MSIL call to Address::set_ZipCode("12345"). As with the get_ZipCode method, attempting to call this method directly in C# generates an error.