The curse of the babble

We talk. We, as developers, software engineers - whatever you call us - we talk. We talk about implementation details, we talk about our toolset, we talk about performance issues. This this a part of our job - if you don't talk about your code, ideas or problems - you have a problem. To be honest, people are just designed like that - you can talk before you can write. However, there is a one specific "talk", which I believe is a curse and hits whole IT really hard.

Have you ever tried to introduce a new tool for your team? It can be a build server, a source control system or a new library. In most cases it is pretty easy - someone gives an idea, you discuss it, point possible problems. Maybe someone is more experienced with this tool and can decide whether it is a go/no-go. Mostly it is a moderate(or not - you know, each dev knows better) discussion and you can close it within an hour, no strings attached. You know why such discussions are the most definite ones? Because devs know the best, what they expect from a tool and what it can give a team.

On the other hand it is not always true, that a team decides. This is especially true for big companies, which involve tons of bureaucracy. You cannot decide on your own - you have to ask your manager, your architect and your director. There are some DevOps engineers and middleware specialists also. Don't forget to ask your mom, your wife and your dog. You have to ask everybody because everybody knows what you need better than you.

Have you mentioned everyone in your email requests? Good, now let them meet and talk. Let them babble, let them discuss all those things they hardly understand. Just imagine:

  • Dev: Hi All, we need a TeamCity instance accessible for us so we can test our .NET apps build process.
  • Mgr: Hi Joe Director, my team wants a TeamCity server so they can work with it.
  • Joe Director: Architect, don't we have such thing in our infrastructure?
  • Architect: No, we have Hudson only - they should be OK with it. Do they want a whole server? They have to ask our server team.
  • Dog: Bark, bark!
  • Dev: I said we want only a TeamCity instance + we want to build .NET apps...
  • Mom: Your grandpa gave a TeamCity your grandma instead of a wedding ring, I will try to find it...
  • Finance: It costs 1,999.00, we can't afford it this year.
  • Joe Director: What?! I have to pay  2000 for this Hudson server?
  • Wife: Are you getting  2000 extra this month? I'll go shopping, wow!
  • Dog: Bark, bark!
  • Mgr: Why do we want to test build process anyway? Can't you just use MSBuild?
  • Dev: We want to test the 'whole' process and by the way - TC is free!
  • Architect: Is it free? Is it enterprise enough? What about integration? Last year we bought enterprise ESB solution for $100K and since we haven't started to implement it yet, I have to know whether this SimCity will work with it.
  • Finance: SimCity? Can we also get it?
  • Dog: Bark, bark!

Sounds familiar? I think everyone knows that feeling.

They will talk for a month considering whether a tool you mentioned is needed for you. They will spend their time discussing, arguing and trying to prove, that they can or cannot afford it. They don't care, that they will spend money while talking. They will spend much more that your tool costs. Funny fact that they don't seem to care about it.

 

Boxing/unboxing - treacherous conversion

Boxing/unboxing conversions are one of the most popular interview questions so I'm not going to explain them in this post(who wants to read another description anyway). Instead I will present one example, which will ensure you, that you understand "what is going on" completely. The example originates from a great book "CLR via C#" by Jeffrey Richter. If you haven't got a chance, I strongly recommend you to read it - it's a fantastic collection of many gotchas in C#/CLR.

Let's say we have following struct in our code:

public struct Point
{
        private int _x;
        private int _y;

        public Point(int x, int y)
        {
            _x = x;
            _y = y;
        }

        public void Change(int x, int y)
        {
            _x = x;
            _y = y;
        }

        public override string ToString()
        {
            return $"{_x},{_y}";
        }
}

(yes, I know that mutable structs are evil - it's not the case). Let's try to play with it and display something:

class Program
{
        static void Main(string[] args)
        {
            var point = new Point(1, 1);
            Console.WriteLine(point);

            point.Change(2, 2);
            Console.WriteLine(point);

            var o = (object)point;
            Console.WriteLine(o);

            ((Point)o).Change(3, 3);
            Console.WriteLine(o);

            Console.ReadLine();
        }
}

The question is - what do you expect a console will display?

1,1

2,2

2,2

3,3

This is what my first thought was like. This is what our intuition tells us. But hey, let's start this program:

1,1

2,2

2,2

2,2

This is something unexpected. How is it possible, that we are missing changing our point to (3, 3)?

The "problem" with this example for most people is, that they forget how unboxing is supposed to work. Casting o to Point doesn't mean, that we are changing its type. We are trying to represent a reference type stored on a managed heap as a value type, which needs to be pushed onto the local thread stack. To do that, compiler has to emit an additional variable, which will store contents of this conversion. Let's check MSIL for this operation:

IL_003a: ldloc.1      // o
IL_003b: unbox.any    Program.Point
IL_0040: stloc.2      // V_2
IL_0041: ldloca.s     V_2
IL_0043: ldc.i4.3     
IL_0044: ldc.i4.3     
IL_0045: call         instance void Program.Point::Change(int32, int32)
IL_004a: nop 

As you can see, compiler emited a V_2 variable, which is supposed to store unboxing result. Then this variable is loaded onto evaluation stack and Change() method is being invoked. Because we don't have any reference to it, we actually don't see, that we are trying to change a Point, that we never expected to be created. Just to make sure, we can check emitted code for writing a result:

IL_004b: ldloc.1      // o
IL_004c: call         void [mscorlib]System.Console::WriteLine(object)
IL_0051: nop   

If we compare it with local variables:

.locals init (
      [0] valuetype Program.Point point,
      [1] object o,
      [2] valuetype Program.Point V_2
 )

we can see, that Console.WriteLine() is being called for an o variable, thus Change() method is never called for it.

Summary

Boxing/unboxing conversion can be treacherous because of the all differences between value and reference types. Above example can be fixed if we use an interface, which declares Change() method - in such case no conversion will be needed. If you are interested in such "not-so-obvious" cases, I strongly recommend you to check ProblemBook.NET book, where you can find even more examples.