DivideByzero
DivideByzero

Reputation: 474

Why some identical strings are not interned in .NET?

string s1 = "test";
string s5 = s1.Substring(0, 3)+"t"; 
string s6 = s1.Substring(0,4)+"";   
Console.WriteLine("{0} ", object.ReferenceEquals(s1, s5)); //False
Console.WriteLine("{0} ", object.ReferenceEquals(s1, s6)); //True

Both the strings s5 and s6 have same value as s1 ("test"). Based on string interning concept, both the statements must have evaluated to true. Can someone please explain why s5 didn't have the same reference as s1?

Upvotes: 5

Views: 301

Answers (5)

willaien
willaien

Reputation: 2797

The CLR doesn't intern all strings. All string literals are interned by default. The following, however:

Console.WriteLine("{0} ", object.ReferenceEquals(s1, s6)); //True

Returns true, since the line here:

string s6 = s1.Substring(0,4)+"";  

Is effectively optimized to return the same reference back. It happens to (likely) be interned, but that's coincidental. If you want to see if a string is interned, you should use String.IsInterned()

If you want to intern strings at runtime, you can use String.Intern and store the reference, as per the MSDN documentation here: String.Intern Method (String). However, I strongly suggest you not use this method, unless you have a good reason to do so: it has performance considerations and potentially unwanted side-effects (for example, strings that have been interned cannot be garbage collected).

Upvotes: 2

adv12
adv12

Reputation: 8551

The Substring method is smart enough to return the original string in the case where the substring being requested is exactly the original string. Link to the Reference Source found in comment by @DanielA.White. So s1.Substring(0,4) returns s1 when s1 is of length 4. And apparently the + operator has a similar optimization such that

string s6 = s1.Substring(0,4)+"";

is functionally equivalent to:

string s6 = s1;

Upvotes: 1

Thomas Levesque
Thomas Levesque

Reputation: 292405

Strings in .NET can be interned. It isn't said anywhere that 2 identical strings should be the same string instance. Typically, the compiler will intern identical string literals, but this isn't true for all strings, and is certainly not true of strings created dynamically at runtime.

Upvotes: 1

lyz
lyz

Reputation: 548

From msdn documentation of object.ReferenceEquals here:

When comparing strings.If objA and objB are strings, the ReferenceEquals method returns true if the string is interned.It does not perform a test for value equality.In the following example, s1 and s2 are equal because they are two instances of a single interned string.However, s3 and s4 are not equal, because although they are have identical string values, that string is not interned.

using System;

public class Example
{
   public static void Main()
   {
      String s1 = "String1";
      String s2 = "String1";
      Console.WriteLine("s1 = s2: {0}", Object.ReferenceEquals(s1, s2));
      Console.WriteLine("{0} interned: {1}", s1, 
                        String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes");

      String suffix = "A";
      String s3 = "String" + suffix;
      String s4 = "String" + suffix;
      Console.WriteLine("s3 = s4: {0}", Object.ReferenceEquals(s3, s4));
      Console.WriteLine("{0} interned: {1}", s3, 
                        String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes");
   }
}
// The example displays the following output:
//       s1 = s2: True
//       String1 interned: Yes
//       s3 = s4: False
//       StringA interned: No

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

You should get false for calls of ReferenceEquals on string objects that are not string literals.

Essentially, the last line prints True by coincidence: what happens is that when you pass an empty string for string concatenation, library optimization recognizes this, and returns the original string. This has nothing to do with interning, as the same thing will happen with strings that you read from console or construct in any other way:

var s1 = Console.ReadLine();
var s2 = s1+"";
var s3 = ""+s1;
Console.WriteLine(
    "{0} {1} {2}"
,   object.ReferenceEquals(s1, s2)
,   object.ReferenceEquals(s1, s3)
,   object.ReferenceEquals(s2, s3)
);

The above prints

True True True

Demo.

Upvotes: 8

Related Questions