Ryan Liu
Ryan Liu

Reputation: 55

Is there a way to check if two string with same value are the same instance in C#?

String in C# is a reference type, but it has overridden ==, and Equals() to compare the value of strings. Is there a way to check if two strings are indeed the same instance and point to the same memory?

Even Object.ReferenceEquals("A", "A") will return true since it just calls ==.

This test will pass. So still waiting to check if the same instance.

  [Test]
        public void TestString()
        {
            var a = "A";
            var b = "A";
            var c = a;
            Assert.IsTrue((object)a == (object)b);
            Assert.IsTrue(ReferenceEquals(a,b));  //It is same as objA == objB
            Assert.IsTrue(Object.ReferenceEquals(a,b));

            Assert.AreEqual(a,b);
            Assert.AreSame(a, b);

            unsafe
            {
                TypedReference tra = __makeref(a);
                IntPtr ptra = (IntPtr)(&tra);


                TypedReference trb = __makeref(b);
                IntPtr ptrb = (IntPtr)(&trb);

                Assert.AreNotEqual(ptra, ptrb);
                Assert.AreNotSame(ptra, ptrb);


                TypedReference trc = __makeref(c);
                IntPtr ptrc = (IntPtr)(&trc);

                Assert.AreNotEqual(ptra, ptrc); 
                Assert.AreNotSame(ptra, ptrc);

                Assert.IsFalse(ptra == ptrc);
            }
            
        }

Upvotes: 0

Views: 734

Answers (1)

Kit
Kit

Reputation: 21709

Your test passes because string literals are automatically interned, meaning they share the same memory. Because they share the same memory, every form of equality is going to return true: ReferenceEquals, Equals, pinning and comparison through a pointer, and ==.

Now, non-literals are not interned, which would make your test fails for ReferenceEquals. For example, for

var a = Console.ReadLine(); // assume user enters cat
var b = Console.ReadLine(); // assume user enters cat

ReferenceEquals will return false. But if you change the code to

var a = string.Intern(Console.ReadLine()); // assume user enters cat
var b = string.Intern(Console.ReadLine()); // assume user enters cat

you're right back to having ReferenceEquals returning true again.

In short, there is most definitely a way to check whether two strings are the same reference using ReferenceEquals or via pointer.

Incidentally, if you interned one of the inputs, that does not imply the other input is also interned, unless you call Intern on both. Interning explicitly is useful for reducing memory when you're likely to work with many strings that are likely to be the same value based on reading console/file input, reading from a database, or otherwise calculating.

Also, string.IsInterned can be useful in some limited scenarios, e.g. maybe for detecting if a string is not interned for some reason when you expected it to be.

Finally, here are a couple of interesting illustrations. Consider

var c = "cat";
var d = new string("cat");
var e = c;

and then these assertions,

Assert.IsTrue(ReferenceEquals(c,d)); // fails, c is interned, d is not
Assert.IsTrue(ReferenceEquals(c,e)); // succeeds, both c and e reference interned "cat"

Upvotes: 9

Related Questions