Summary: System.String consistently uses the most space on the heap in the widest variety of applications -- good string management is necessary if this is on your key performance paths
See
PerfSystemTextStringBuilder for additional comments.
System.String Notes
General Notes on String Sharing
System.String is almost always the number one consumer of memory in applications... small wonder with so many useful
APIs producing and consuming strings. Many applications frequently process the same or similar strings over and over -- in the context of parsing for instance the strings "0", "1", "true", and "false" can appear very often in the input and often each results in its own string object. When parsing from (e.g.) a Stream, consider keeping a short list of frequently used strings to increasing the sharing of identical strings.
Interning
public static string Intern(string str);
Interning of strings should be done cautiously because it creates string objects which effectively live forever. In many cases this is not appropriate because the strings that you would like to share are more properly scoped to (e.g.) a transaction. Additionally, to get the interned version of a string you must first have already made a non-interned version of the string. If you are trying to reduce your overall allocation rate then String.Intern likely won't be helpful
Splitting
public string[] Split(params char[] separator);
public string[] Split(char[] separator, bool [omitEmptyEntries);]
etc.
This is a very convenient but costly API. Necessarily it will have to allocate an array for the output and one string object for each substring in the split result, not to mention any temporary storage it needs for its own use. In many cases (such as validation) you can avoid splitting by using these two
APIs in concert:
public int [IndexOf(char] value, int startIndex);
public static int Compare(string strA, int indexA, string strB, int indexB,
int length, bool ignoreCase, [CultureInfo] culture);
These allow you to march along the parts of a string in place and then do whatever testing you need to do without allocating any substring objects or arrays.
Be careful creating a lot of temporary garbage
You never can be sure what you'll find when you browse random source... today I found this:
// Count the tabs
int tabs = 0;
// input known to have zero or more leading tabs and at least one other character
string tabber = input;
while (tabber.Length > 0 && (tabber.StartsWith("\t")))
{
tabs++;
tabber = tabber.Substring(1);
}
// ... tabber is not further used...
This much simpler code would have been fine:
int tabs = 0;
while (each[tabs] == '\t')
tabs++;
Fancy string primitives give you lot of rope to hang yourself with.