2

I was unable to find anything that expressly answered my question so I thought I would ask: Is it best practice or just preference to do something like:

public static class FixedStrings
{
      public static readonly string foo = "bar";
}

class Example1
{
    void UseString()
    {
        Console.WriteLine(FixedStrings.foo);
    }
}

Instead of:

class Example2
{
    void UseString()
    {
        Console.WriteLine("bar");
    }
}

This is my current understanding (I could be wrong):

  1. It is a trade-off of memory vs GC allocations; so the first example is the better choice if the method requiring the string is called often, or it is in a long running process.
  2. If memory is not an issue, caching strings (or any objects) that can be reused will reduce time the GC takes and increase responsiveness of program.
  3. The .NET GC does a pretty good job, but periodically a couple of those strings will get through the generations especially if under load and after a long time may cause full GC cleanup.
  4. Example1 beats Example2 pretty much always in terms of performance.

I noticed in example code from various places, people use both. I am unsure if it is just because example2 is quicker to type or if because of reading preference. I am also curious, is the compiler smart enough to automatically turn some of these strings into cached variables?

Please keep in mind I say all of this with just what I have learned from the internet, I have no formal education. I may be very wrong about assuming example1 is better

Here is why I worry about such a minor optimization:

It is not so minor in my actual project; in some cases I may be using these strings thousands of times per second.

Buretto
  • 405
  • 4
  • 12
  • Technically, having the form _"Is it best practice or just preference to do something like"_, your question is inappropriate due to being "primarily opinion-based". However, giving the benefit of the doubt from the side-question asking whether strings are cached, closing as a duplicate also seems reasonable. See marked duplicates for details that address your overall question. – Peter Duniho Mar 12 '19 at 04:47
  • 1
    If you suspect that the impact is not minor in your actual project then **you should be able to measure that impact**. Write the code both ways, run it both ways, and see which, if either, has unacceptable performance. If you're not able to detect unacceptable performance then... then I suppose it is acceptable, isn't it? – Eric Lippert Mar 12 '19 at 16:56

1 Answers1

4

I am also curious, is the compiler smart enough to automatically turn some of these strings into cached variables?

Yes it is. If you have identical string literals in a single assembly, C# will, as an optimization, only create one instance of that string at runtime and cache it. This is called "string interning". This blog post by Eric Lippert explains the general principle and the implications it can have for performance / garbage collection.

As a result of string interning, both of your examples result in just one string allocation at runtime, assuming all code in those examples exists within a single assembly.

Therefore which pattern you follow is a matter of style. For examples used in documentation and education, it's typically easier to read if the string is "in line" with the rest of the code, like Example 2. If you're writing a large, complex program where the exact same string must be used in multiple places, I would prefer Example 1. This avoids having to retype the string - possibly mistyping it - at each place it is used, gives a meaningful name to the string within your code, and allows you to edit only one line if the string ever needs to be changed in a future version.

Finally, about the project you mention. If the strings you are concerned about are defined at compile-time (i.e., they are string literals like "My String"), then C#'s automatic string interning may suffice. If you have a lot of strings that you don't know until runtime, but which you want to avoid re-allocating, you may want to declare something to hold those string objects for later use by your code. What kind of data structure you use depends on your use case.

You can also force the runtime to intern a string by calling String.Intern(String). This method puts the given string into the string interning cache (if it is not already there) and returns the string from the cache. Note that you will still need to allocate the string somehow to pass it into this method, but the lifetime of that instance may be a lot shorter and therefore make your project perform better.

Joe Sewell
  • 6,067
  • 1
  • 21
  • 34