I recently came across an interesting little C# gotcha in a Pluralsight video[1] that I thought I’d share.
Consider a class library with this, badly written, data access class:
public class DataAccess
{
public const int MaxAllowedConnections = 3;
public int OpenConnectionCount
{
get
{
// simulate a few already open connections
return 2;
}
}
public void OpenConnection()
{
if (OpenConnectionCount >= MaxAllowedConnections)
{
throw new InvalidOperationException(
"Too many connections!");
}
}
}
The class exposes the constant MaxAllowedConnections and the property OpenConnectionCount to allow clients to check before trying to open a connection, and so avoid the exception. For example in this little console app:
public class Program
{
public static void Main(string[] args)
{
try
{
var dataAccess = new DataAccess();
if (dataAccess.OpenConnectionCount <
DataAccess.MaxAllowedConnections)
{
dataAccess.OpenConnection();
Console.Out.WriteLine("Connection opened");
}
else
{
Console.Out.WriteLine(
"No available connections");
}
}
catch (Exception exception)
{
Console.Out.WriteLine(
"Oops! {0}", exception.Message);
}
}
}
It all looks “good”, and if we compile and run it, we get the expected output:
Connection opened
Now if we were to change MaxAllowedConnections to 2 and recompiled both the library and client, then everything is still good, and the output is as now expected, i.e.
No available connections
However if instead we had made the change to MaxAllowedConnections but only recompiled the library, e.g. like getting a “compatible” minor update from a third party, or patching our own system, then we get an unexpected error:
Oops! Too many connections!
The problem is that the C# compiler will replace all usages of MaxAllowedConnections with the literal value at compile time. This applies to all usages, not just within the library. When we first compiled the client, the compiler replaced MaxAllowedConnections with the literal value 3, permanently embedding the value in the assembly. Recompiling the library updated its value for the constant to 2 but had no effect on the client. Hence the check in the library is now:
if (dataAccess.OpenConnectionCount < 2)
{
// ...
}
but the check in the class is still:
if (dataAccess.OpenConnectionCount < 3)
{
// ...
}
Links
- Pluralsight online training course “Lessons from Real World .NET Code Reviews” : http://app.pluralsight.com/courses/dotnet-code-reviews-real-world-lessons