>>1
Now try it with
object d = "1"; d += "0";. It's false again!
First you need to understand the difference between value types and reference types.
Value types are: int, double, char, TimeSpan etc plus user-defined structures.
Reference types are: string, object, plus all user-defined objects.
The variable of a value type holds the value itself. The value of a reference type holds a reference to a value allocated somewhere else. When you declare an int field in an object, it names the four bytes withing that object where the value is stored, or the four bytes on the stack for a local variable inside a function. When you declare a string field or variable, that variable names a four or eight bytes of a pointer, that holds an address of the actual value of the string which is stored somewhere else.
The primary difference is that when you write "a = b", if both are value types then the value of the `b` variable is copied into the `a` variable and now you have two separate copies of the value. If both are reference types, then the reference is copied, and now both point at the same value.
You shouldn't confuse that with immutability, which is an orthogonal concept. I mean, the litmus test to determine what we are dealing with, two separate but equal values or two variables pointing at the same value, is to modify the value of the variable `a` and see if the value of `b` changes. But if `a` and `b` are strings you can't do that, strings are immutable, when you write `a += b` it actually means `a = a + b` -- i.e. a new string object is created and its address is stored in `a`.
Then. When you assign a value of a value type (such as an integer) to a variable of compatible reference type (such as System.Object, other variants include user-defined structures that implement some interface, and a variable of that interface type) then the magical autoboxing happens: an new value is created on the heap and the variable is assigned a reference to that value. The name means that a "box" object is created and the value is stored inside it, automatically.
Note that while the value of plain int takes 4 bytes, the object that holds that value after boxing is rather huge -- it contains a pointer to its class (System.Int32) among other things. Also note that a boxed int is immutable too!
Then. The operators like `==` are resolved at compile time, based on the types of the variables involved, not of the objects that the variables (if they are of reference types) are referencing. Which means that if you compare two variables of type int, then int's comparison operator is invoked, and it compares values. When you compare two variables of type string, then string's comparison operator is invoked, and it goes through references and again compares values.
But when you compare two variables of type object, then object's comparison operator is invoked, and it compares references themselves. Which are different in case of two boxed ints. But which are the same in case of two _constant_ strings -- the compiler notices that the values are the same (even if you write it as "1" + "0"), stores the single value which is then referenced by both your variables. And which are again different if you create a new string with the same value at runtime.
Finally, the last puzzling question is, how the fuck `c += "0"` works if `c` is an object? Well, first, compiler transforms that into `c = c + "0"`. Then it sees that System.String (the constant "0") happens to define an addition operator with an object on the left side (which calls its .ToString()). The call to which it happily compiles then.
Your welcome!