Working with String Data
System.String provides a number of methods you would expect from such a utility class, including methods that return the length of the character data, find substrings within the current string, and convert to and from uppercase/lowercase.
Properties
| Length | This property returns the length of the current string. |
Methods
| Contains(string) | This method determines whether a string contains a specific substring. |
| Equals(string) | Determines whether this instance and another specified string object have the same value. |
| Insert(int, string) | Returns a new string in which a specified string is inserted at a specified index position in this instance. |
| Remove(int) | Returns a new string in which all the characters in the current instance, beginning at a specified positing and continuing through the last position, have been deleted. |
| Replace(string, string) | Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string. |
| StartsWith(string) | Determines whether the beginning of this string instance matches the specified string. |
| EndsWith(string) | Determines whether the end of this string instance matches the specified string. |
| ToLower() | This method create a copy of the current string in lowercase. |
| ToUpper() | This method create a copy of the current string in uppercase. |
| Trim() | Removes all leading and trailing white-space characters from the current string object. |
| Substring(int) | Retrieves a substring from this instance. The substring starts at a specified character position and continues to the end of the string. |
| Substring(int,int) | Retrieves a substring from this instance. The substring starts a specified character position and has a specified length. |
| IndexOf(string) | Reports the zero-based index of the first occurrence of the specified string. |
Be aware that a few of the members of System.String are static members and are, therefore, called at the class (rather than the object) level.
Performing String Concatenation
String variables can be connected to build larger strings via C# "+" (as well as +=) operator. As you might know, this technique is formally termed "string concatenation".
string s1 = "Programming the ";
string s2 = "PTP";
string s3 = s1+s2; C# "+" symbol is processed by the compiler to emit a call the static String.Concat() method. Given this, it is possible to perform string concatenation by calling string.Concat() directly as shown:
string s1 = "Programming the";
string s2 = "PTP";
string s3 = string.Concat(s1,s2); Using Escape Characters
Each escape character begins with a backslash.
| \' | Inserts a single quote into a string literal. |
| \" | Inserts a double quote into a string literal. |
| \\ | Inserts a backslash into a string literal. |
| \a | Triggers a system alert(beep). |
| \n | Inserts a line feed (Unix-based systems) |
| \r\n | Inserts a line feed (Non-Unix-based systems) |
| \r | Inserts a carriage return. |
| \t | Inserts a horizontal tab into the string literal. |
Performing String Interpolation
C# 6; C# programmers can use an alternative syntax to build string literals that contain placeholders for variables, this is called "string interpolation".
string name = "C# language";
// using curly-braces
string greeting = string.Format("Hello {0}", name);
// using string interpolation
string greeting_2 = $"Hello {name}";
Performance Improvements (Updated 10.0)
When using string interpolation in versions prior to C#10, under the hood the compiler is converting the interpolated string statement into a Format() call.
Previous examples will be converted Format() call.
string name = "C# language"
// using curly-braces
string greeting = string.Format("Hello {0}",name);
// using string interpolation
string greeting_2 = $"Hello {name}";
The problem with this is performance. When Format() is called at runtime, the method parses the format string to find the literals, format items, specifiers, and alignments, which the compiler already did at compile time. all items are passed in as System.Object, which means value types are boxed. If there are more than 3 parameters, an array is allocated. In addition to performance issues, Format() works only with reference types.
A major change in C#10 is that all the work that can be done at compile time is retained in the IL using the "DefaultInterpolatedStringHandler(int, int)" and its methods.
string name = "dotnetguard";
int age = 4;
var builder = new DefaultInterpolatedStringHandler(3,2);
builder.AppendLiteral("\tHello ");
builder.AppendFormat(name);
builder.AppendLiteral(" you are ");
builder.AppendFormatted(age);
builder.AppendLiteral(" years old");
var greeting = builder.ToStringAndClear();
Console.WriteLine(greeting);
The version of the DefaultInterpolatedStringHandler's constructor used in this example takes 2 integers. The first is the number of literals, and the second is the number variables. This enables the instance to make an educated guess as to how much memory to allocate. Literals are added in with AppendLiteral() method, and variables are added in with the AppendFormatted() method.
Defining Verbatim Strings (Updated 8.0)
When you prefix a string literal with the "@" symbol, you have created what is termed a verbatim string. Using verbatim strings, you disable the processing a literal escape characters and print out a "string" as is. This can be most useful when working with strings representing directory and network paths.
Console.WriteLine(@"C:\Users\DotnetGuard\");
Also verbatim strings can be used to preserve whitespace for string that flow over multiple lines.
string myLongString = @"This is a
very
very long
string";
You can also directly insert a double quote into a literal string by doubling " taken.
Console.WriteLine(@"Cerebus said "" Darrrr! Pretty sun-sets """);
Verbatim strings can also be interpolated strings, by specifying both the interpolation operator ($) and the verbatim operator (@).
string interp = "interpolation";
string myLongString2 = $@"This is a very
very
long string with {interp}";
Note : C# 8, the order does not matter using either $@ or @$ will work.
Working with Strings and Equality
When you perform a test for equality on reference types (via the C# "==" and "!=" operators), you will be returned true if the references are pointing to the same object in memory. However, even though the string data type is indeef a reference type, the equality operators have been redefined to compare the values of string objects, not the object in memory to which they refer.
string s1 = "Hello!";
string s2 = "Yo!";
Console.WriteLine(s1 == s2); // False
Console.WriteLine(s1 == "Hello!"); // True
Console.WriteLine(s1 == "HELLO!"); // False
Console.WriteLine(s1 == "hello!"); // False
Console.WriteLine(s1.Equals(s2)); // False
Console.WriteLine("Yo!".Equals(s2));// True Modifying String Comparison Behavior
A much better practice is to use the overloads of the methods listed earlier that take a value of the StringComparison enumeration to control exactly how the comparisons are done.
Values of the StringComparison Enumeration
| CurrentCulture | Compares strings using culture-sensitive sort rules and the current culture. |
| CurrentCultureIgnoreCase | Compares strings using culture-sensitive sort rules and the current culture and ignores the case of the strings being compared. |
| InvariantCulture | Compares strings using culture-sensitive sort rules and the invariant culture. |
| InvariantCultureIgnoreCase | Compares strings using culture-sensitive sort rules and the invariant culture and ignores the case of the strings being compared. |
| Ordinal | Compares strings using ordinal (binary) sort rules. |
| OrdinalIgnoreCase | Compares strings using ordinal (binary) sort rules and ignores the case of the strings being compared. |
// ========== 1. ORDINAL vs IGNORECASE ==========
string a = "DotNetGuard";
string b = "dotnetguard";
Console.WriteLine(a.Equals(b, StringComparison.Ordinal));
// False — D and d are different bytes
Console.WriteLine(a.Equals(b, StringComparison.OrdinalIgnoreCase));
// True — ignores case difference
// ========== 2. CULTURE MATTERS ==========
string x = "İSTANBUL";
string y = "istanbul";
Console.WriteLine(x.Equals(y, StringComparison.CurrentCultureIgnoreCase));
// True — on a Turkish system, İ↔i maps correctly
// On an English culture system:
// İ is not recognized, produces different result — bug source
// ========== 3. CLASSIC TURKISH BUG ==========
string user = "INFO";
string check = "info";
Console.WriteLine(user.Equals(check, StringComparison.InvariantCultureIgnoreCase));
// True — I and i match (English rules)
// But in Turkish culture:
// "INFO".ToLower() → "ınfo" (I becomes ı in Turkish)
// "ınfo" == "info" → False!
// NEVER compare strings using ToLower()
// ========== 4. WHEN TO USE WHICH? ==========
Console.WriteLine("HTTP".Equals("http", StringComparison.OrdinalIgnoreCase));
// Use Ordinal — no culture interference, fastest
string[] cities = { "Çanakkale", "Adana", "İstanbul", "Bolu" };
Array.Sort(cities, StringComparer.CurrentCulture);
// Adana, Bolu, Çanakkale, İstanbul — sorted by Turkish alphabet
foreach (var c in cities)
Console.WriteLine(c);Strings are Immutable
After you assign a string object with its initial value, the character data cannot be changed. If you look more closely at what is happening behind the scenes, you will notice them methods of the string type are, in fact, returning you a new string object in a modified format.
string s1 = "This is my string";
Console.WriteLine(s1);
string upperString = s1.ToUpper();
Console.WriteLine(upperString);
string s2 = "My Other String";
s2 = "new string value";
Console.WriteLine(s2);
Note that the 2 calls to the ldstr(load string) opcode, the ldstr opcode of the CIL loads a new string object on the managed heap.
Note: If you are building an application that makes heavy use of frequently changing textual data, it would be a load idea to represent the word processing data using string objects, as you will most certainly end up making unneccessary copies of string data.
Using the System.Text.StringBuilder Type
Like the System.String class, the StringBuilder defines methods that allow you to replace or format segment.
using System.Text;
What is unique about the StringBuilder is that when you call members of this type you are directly modifying the object's internal character data (making it more efficient), not obtaining a copy of the data in a modified format.
StringBuilder sb = new StringBuilder();
sb.Append("\n");
sb.AppendLine("Half Life");
sb.AppendLine("Deus Ex" + "2");
Console.WriteLine(sb.ToString());
sb.Replace("2", "Invisible War");
Console.WriteLine(sb.ToString());
By default, a StringBuilder is only able to initially hold a string of 16 characters or fewer (but will expand automatically if necessary); however, this default starting value can be changed via an additional constructor argument.
StringBuilder sb = new StringBuilder("Fantastic Games", 256);
If you append more characters than the specified limit, the StringBuilder object will copy its data into a new instance and grow the buffer by the specified limit.