+5  A: 

Learnt a new thing today.

I guess Jon said in one of the questions, I tried to answer.

When you build a string using concatenation, == will return true for 2 strings of matching value but they don't point to the same reference (which I thought, it should due to string interning. Jon pointed that string interning works for constant or literals).

In the generic version, it is calling object.ReferenceEquals (which is different than ==. In case of string, == does value comparison).

As a result, the concatenated version returns false whereas the constant (literal string) version returns true.

EDIT: I think Jon must be around to explain this in a much better way :)
Lazy me, I have bought the book but have yet to get started on it. :(

shahkalpesh
No need for me to explain it myself - Marc's done a great job :) (and your explanation is fine too).
Jon Skeet
+10  A: 

On the case of strings, you probably don't intend to use reference equality. To access equality and inequality in generic methods, your best bet it:

EqualityComparer<T>.Default.Equals(x,y); // for equality
Comparer<T>.Default.Compare(x,y); // for inequality

i.e.

static bool AreValuesEqual<T>(T first, T second)
    where T : class
{
    return EqualityComparer<T>.Default.Equals(first,second);
}

This still uses the overloaded Equals, but handles nulls etc too. For inequality, this handles nulls, and both IComparable<T> and IComparable.

For other operators, see MiscUtil.


Re the question; in the case of:

    string intro1 = "My name is Jon";
    string intro2 = "My name is Jon";
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

You get true, true because the compiler and runtime is designed to be efficient with strings; any literals that you use are "interned" and the same instance is used every time in your AppDomain. The compiler (rather than runtime) also does the concat if possible - i.e.

    string intro1 = "My name is " + "Jon";
    string intro2 = "My name is " + "Jon";
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

is exactly the same code as the previous example. There is no difference at all. However, if you force it to concatenate strings at runtime, it assumes they are likely to be short-lived, so they are not interned/re-used. So in the case:

    string name = "Jon";
    string intro1 = "My name is " + name;
    string intro2 = "My name is " + name;
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

you have 4 strings; "Jon" (interned), "My name is " (interned), and two different instances of "My name is Jon". Hence == returns true and reference equality returns false. But value-equality (EqualityComparer<T>.Default) would still return true.

Marc Gravell
+1. As always well explained.
shahkalpesh
@Marc: Thanks, that was succinct
AB Kolan
+2  A: 

It has nothing to do with the generic method but the instantiation of the strings

in the first version of main you have:

string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;

which creates 4 strings. Two of which are compile time constants namely "Jon" and "My name is" however when initializing intro1 and intro2 the compiler cannot say that name is always jon and resolves the value runtime making a new string for each of intro1 and intro2.

in the second version

string intro1 = "My name is Jon";
string intro2 = "My name is Jon";

you only have one string and thats a compile time constant: "My name is Jon" and you assign that string to both intro1 and intro2 and that's why

AreReferencesEqual(intro1, intro2)

returns false in the first case and true in the second

Rune FS