6
string s = "";
for(int i=0;i<10;i++) {
s = s + i;
}

I have been these options to answer this question.

  1. 1
  2. 11
  3. 10
  4. 2

I have this simple code, I just want to know how many string objects will be created by this code.

I have a doubt, Is string s = ""; creates no object. I dont think so, Please make me clear.

If I append string with + operator, it creates new string, so I think It will be a new object created in every iteration of for loop.

So I think there will be 11 objects created. Let me know If I'm incorrect.

String result = "1" + "2" + "3" + "4";  //Compiler will optimise this code to the below line.
String result = "1234"; //So in this case only 1 object will be created??

I followed the below link, but still its not clear.

Link1

Please cover string str and string str = null case too. What happens If we dont initialize string and when If I assign string to null. So It will be an object or no object in these two cases.

string str;

string str = null;

Later in the code, If I do.

str = "abc";

Is there any programming way to calculate number of objects?, because I think It may by a debatable topic. How can I be 100 % by doing some programming or by some tool? I cannot see this in IL code.

I have tried the below code,just to make sure whether new object is created or not. It writes 'different' for each iteration. It means it always gives me a different object, So there can a possibility of 10 or 20 objects. because it does not give me info of intermediate state(boxing for i when doing s = s + i)

    string s = "0";
    object obj = s;
    for (int i = 0; i < 10; i++)
    {
        s = s + i;

        if (Object.ReferenceEquals(s, obj))
        {
            Console.Write("Same");
        }
        else
        {
            Console.Write("Different");
        }
    }

I'm not agreed by the statement that string str = "" does not create any object. I tried this practically.

    string s = null;
    object obj = null;

    if (Object.ReferenceEquals(s, obj))
    {
        Console.Write("Same");
    }
    else
    {
        Console.Write("Different");
    }

Code writes "Same", but If I write string s = "";, It writes "Different" on console.

I have one more doubt now.

what is difference between s = s + i and s = s + i.ToString().

s = s + i.ToString() IL Code

IL_000f:  call       instance string [mscorlib]System.Int32::ToString()
IL_0014:  call       string [mscorlib]System.String::Concat(string, string)

s = s + i IL Code

IL_000e:  box        [mscorlib]System.Int32
IL_0013:  call       string [mscorlib]System.String::Concat(object, object)

So Whats difference between box and instance here

Community
  • 1
  • 1
Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • 1
    Try `ReferenceEquals("", string.Empty)`. `string s = null;` doesn't store a reference to a string, it stores a `null` reference, whereas `string s = "";` stores a reference to the *existing* empty string object, no *new* object was created for that statement. – Lasse V. Karlsen Nov 10 '16 at 10:25
  • 1
    To your last update. Since `string` is immutable class you *can* share the instances (i.e. cache them), but, sure, don't *have to* do it. Empty string is *very* popular instance, that's why it's cached *explictly* as `string.Empty`. `string s = "0";` is *not* that frequent and .Net doesn't cache it as a *special case*. – Dmitry Bychenko Nov 10 '16 at 10:25
  • 1
    Also try this: `string s1 = "test"; string s2 = "test"; ReferenceEquals(s1, s2)`, the last expression will return `true` since string literals are interned and reused by the JITter. Only 1 new string object was created because of this code, a single string object holding the text `"test"`. – Lasse V. Karlsen Nov 10 '16 at 10:26
  • `int` is a struct, which passes by value, `object` passes by reference; `boxing` creates a wrapping `object` over struct `int` (similar to `Integer` and `int` in Java) see http://stackoverflow.com/questions/2111857/why-do-we-need-boxing-and-unboxing-in-c; `i.ToString()` is just a method – Dmitry Bychenko Nov 10 '16 at 14:03
  • is wrapping object means creating new object? So when I change from value type to reference type, does new object get created ? – Vivek Nuna Nov 10 '16 at 14:06

2 Answers2

6

Well, let's count:

string s = ""; // no new objects created, s assigned to string.Empty from the cache

// 10 times:
for(int i = 0; i < 10; i++) {
  // i     <- first object to create (boxing): (object) i
  // s + i <- second object to create: string.Concat(s, (object) i);
  s = s + i;
}

To test that string s = "" doesn't create an additional object you can put

string s = "";

if (object.ReferenceEquals(s, string.Empty))
  Console.Write("Empty string has been cached");

finally, we have 20 objects: 0 + 10 * 2 (10 boxed ints and 10 strings). In case of

string result = "1" + "2" + "3" + "4";

as you can see the result can be and (will be) computed at the compile time, so just one object ("1234") will be created. In case of

string str; // just a declaration, str contains trash

string str = null; // no objects created
...
str = "abc"; // an object ("abc") created
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • But I have not been given option 20 – Vivek Nuna Nov 10 '16 at 10:00
  • 6
    Then the options are wrong. Additionally, `i` will be boxed for each iteration which will create an additional *object* (not a string though). – Lasse V. Karlsen Nov 10 '16 at 10:00
  • In your first block of code surely `string s = "";` would create a string object, just one that contains 0 characters? – sr28 Nov 10 '16 at 10:02
  • 1
    @sr28: `""` is cached as `string.Empty`; you can convince youself by `if (Object.ReferenceEquals(s, string.Empty)) Console.Write("cached");` – Dmitry Bychenko Nov 10 '16 at 10:03
  • Not sure I understand. If I check MSDN it says "An empty string is an instance of a System.String object that contains zero characters". I read this to mean that this creates a new string instance. – sr28 Nov 10 '16 at 10:15
  • 2
    @sr28: yes, and `string.Empty` fits this definition (contains zero symbols); since `string` is *immutable* class, you can *share* the instances, i.e. don't create them but take from cache – Dmitry Bychenko Nov 10 '16 at 10:17
  • @DmitryBychenko Thanks for your answer. I have edited my question with example, because I dont think that "no object create when I do string str = "" " – Vivek Nuna Nov 10 '16 at 10:23
  • 3
    String literals in code are interned by the JITter at JITting time. Yes, a string object is created to hold the empty string, but it isn't created *by the code in the question*. It was created way earlier when the program started running, by the runtime. Whether this code was present at all makes no difference, the empty string was still created. As such, since the question ask "by below code", then the empty string doesn't count. It wasn't created *by that code*. Instead, a reference to the existing empty string was used instead which did not create another string object in memory. – Lasse V. Karlsen Nov 10 '16 at 10:23
  • 1
    Lasse V. Karlsen is right. Strings are cached. When you create a string which has the same content than another string that already exists you only have 1 object(2 references to that 1 object). This works because string is immutable which means you can not change it. Whenever you try to change it you create a new instance (or reference another if an already existing string equals another yours after change) – Dominik Nov 10 '16 at 10:33
  • @LasseV.Karlsen, It was very nice discussion with you and Dmitry Bychenko – Vivek Nuna Nov 10 '16 at 10:39
  • Bear in mind that if you somehow trick something to manually construct a string which also happens to be empty, without using a string literal, you may end up with another empty string object in memory. The JITter or .NET is pretty "smart" though so this, for instance - `string s = new string(' ', 0);` - won't create another string object, it will still reference the empty string object that already exists. This may also be the result of the string constructor being external code that simply *returns* the empty string reference though so it may not be the JITter in this example. – Lasse V. Karlsen Nov 10 '16 at 11:04
  • 1
    @DmitryBychenko - if you disassemble the code generated then you will see that i.ToString() is not called, the int is boxed & then concat is called - if a string object is created it is within the .Net code & can be ignored just as the creation of the empty string can. Replacing s=s+i; with s=s+i.ToString(); does explicitly create a new string before calling concat. – PaulF Nov 10 '16 at 13:04
  • @PaulF: thank you! I see! I should have been more accurate when dealing with / commenting low level things. I've edited the answer. – Dmitry Bychenko Nov 10 '16 at 13:10
  • @DmitryBychenko, so it will be 10 objects? – Vivek Nuna Nov 10 '16 at 13:49
  • @vivek nuna: it will be `20` objects, with `10` `string` and `10` boxed `int`s among them – Dmitry Bychenko Nov 10 '16 at 13:50
  • @LasseV.Karlsen any suggestions on my latest update in question ? – Vivek Nuna Nov 10 '16 at 13:58
  • Just consider whether you're invalidating any existing answers here because nobody likes posting answers for a moving-target-question. – Lasse V. Karlsen Nov 10 '16 at 14:36
  • The difference between the two calls to String.Concat is that in the .Object version both parameters will be converted to strings through a call to .ToString(), since one of the two objects is already a string, its ToString method will simply return `this`, in which case the actual number of strings won't differ at all. On the other hand, for the variant which calls .ToString on the int on the outside, no boxing of the int will be done, so same number of strings, one less *object* created. – Lasse V. Karlsen Nov 10 '16 at 14:37
0

Although Dmitry Bychenko answer is right and well explained, But I want to add something.

string s = "";
for(int i=0;i<10;i++) {
s = s + i;
}

Code will give you 20 objects. No object will be created for string str = "", because reference will have cached empty string. And s = s + i; will be expended to the following IL code, So its giving guaranty that boxing will happen and new object will be created to refer new string s + i

IL_000e:  box        [mscorlib]System.Int32
IL_0013:  call       string [mscorlib]System.String::Concat(object, object)

You can see IL code by using IL Disassembler.

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • 1
    Note that s=s+i.ToString(); generates different code to s=s+i; and does create a new string - so the answer to your original question should be 10 String objects are created (along with 10 boxed ints). If you used s=s+i.ToString() then 20 string objects are created. – PaulF Nov 10 '16 at 13:01
  • @PaulF so you mean when I do s = s+ i, only boxing happens but it does not create new opbject. When we do i.ToString it creates new object?? – Vivek Nuna Nov 10 '16 at 13:51
  • What happens with s=s+i is that a new boxed int object is created & this is concatenated with the original string - internally a new string object maybe created from the boxed int - but I would consider that the same way that creation of the empty string has been (ie ignored). In the case of s=s+i.ToString(); then a new string object is explicitly created & the two strings concatenated. I have always considered s=s+i as just a short-hand for s=s+i.ToString() - so I was surprised to see completely different code generated. – PaulF Nov 10 '16 at 15:08