Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

Be careful of compile time code changes

by Ioannis Panagopoulos

In my top ten list of difficult bugs I had to solve in C# in my professionali carreer reside two that both had to do with compile time code changes that I was not aware of. In this post I will describe both of them in detail in the hope that it will help someone out there searching for a solution to his own issue.

Difference between properties with public accessors and public fields.

What is the difference between declaring a property with public default accessors:

public string demo {get;set;}

Compared to declaring the same variable as a field?

public string demo;

It seems that there is none and we can use any one of the two with not much difference. Actually, one may argue that the usage of a "field" may be a littler faster since it does not lead to the generation of a method call as the usage of a "property" does. In other words if we are to disassemble the generated code (using ildasm) you will see that the "field" declaration produces just a field (Class2) while the "property" variable results in a field and two methods (Class1) :

While the "property" declaration results in a method call

Now consider the following case. Say you have in a solution two projects. A "class library" project for your application's logic and a "console application" project which is the executable of your application. Now, let's suppose that the variable in question (named demo) resides in a class (DemoClass) in the "class library" project.The "demo" variable is used in the "console application".

We initially declare our "demo" variable as a field and we deploy the .dll and .exe files. Everything works fine.

Now a requirement comes that leads us to change the "demo" variable in the .dll from a "field" to a "property" since we need to implement some logic in its accessors (get and set). So we perform the change, test the project in Visual Studio and verify that it works and since we have not made any changes in the "console application" we just redeploy the .dll and not the .exe file. We will soon realize that the application will stop working. So while we test it in VS it works and when we deploy the .dll it does not.

The reason behind this lies in the fact that initially when the demo variable was implemented as a field, the code that was using the field demo in the "console application" was created as follows (note the stfld assembly command that you can interpret as "store in field"):

If the variable was implemented as a property then the code would be different for the same action (callvirt actually means call the function set_demo:

So changing the field to a property and deploying just the .dll will not change the code in the user of the field/property and most definitely lead to a difficult to locate That is the reason why public default accessors exist and why you should most definitely use them.

Creating a method with default arguments

Say now that you have a method in your .dll that you use in another project (.dll or whatever) numerous times in various places. Let's suppose that this method is as follows:

public void Add(int op1,int op2){
	// Your logic here
}

You compile the project and deploy the .dll and the other .dll,.exe or whatever modules in the client.

A requirement comes that leads you to need to add another argument in the "Add" method. Since there are many place where this method is used in most frequently this extra argument will not be needed in those cases you resort to an argument with a default value, that is (the argument "extra" with the default value of 0):

public void Add(int op1,int op2,int extra=0){
	// Your logic here
}

And you redeploy the whole project. Everything works well. Now an extra requirement comes that leads you to conclude that the default value of extra should not be 0 but 1. So you change the default value and since you have not made any other change you just deploy that single .dll. You will soon realize that the default value has not changed even if you verify that you did it in your code.

The reason behind this is the fact that during "compilation" the default values of arguments are injected to the callers. Therefore if the caller is in another dll, you change the default value of the argument and just deploy the method's dll, the code of the caller will not change and therefore will still be carrying the old value! Beware that this is an even worse bug since it does not lead to a program crash rather to an unwanted and possibly unnoticed behavior. The screenshot below proves that the default value is injected to the caller (this code is taken from the caller's dll where you see that the ldc.i4.0 caused by the default value of the argument has been injected here).

So in this case the moral of the story is to never use default values in your method's arguments or if you do be extremely careful with such scenarios.

blog comments powered by Disqus
hire me