Whether you’re still learning C# or you already have some experience, there will always be gaps in your knowledge, places where you are clueless as to your ignorance.

In the series of articles in C# Tips I will provide a whole host of useful information about the sometimes underused or unknown features of both C# and .NET.

In this article, we’re going to be learning about a whole host of different tips and tricks related to C# String and Formatting.

C# String and Formatting Tips

Simplifying String Empty and Null Checking Code

Let’s start with some code,

1 using System ; 2 using static System . Console ; 3 4 namespace string_formatting_tips 5 { 6 class Program 7 { 8 static void Main ( string [] args ) 9 { 10 while ( true ) 11 { 12 WriteLine ( "Enter your name:" ); 13 14 string name = ReadLine (); 15 16 if ( name == null || name == "" || name . Trim () == "" ) 17 { 18 WriteLine ( "Please enter your name" ); 19 continue ; 20 } 21 22 WriteLine ( "Hello!" + name ); 23 break ; 24 } 25 26 ReadLine (); 27 } 28 } 29 }

The above code reads the user input and checks if the user has entered any value. It also makes sure that the user hasn’t typed in a few spaces and hit Enter using the Trim() method.

1 name . Trim () == ""

The first tip we’re going to look at is to simplify the above code, and what we’re going to do is actually just delete all of the conditional code:

1 if ( name == null || name == "" || name . Trim () == "" ) 2 { 3 ... 4 }

and instead, we’re going to make use of the string.IsNullOrWhiteSpace method.

1 if ( String . IsNullOrWhiteSpace ( name )) 2 { 3 ... 4 }

This string.IsNullOrWhiteSpace method returns true if the input string is null and an empty string or contains only whitespace characters, such as spaces and tabs.

There’s also another convenience method on the String class here, and that’s the IsNullOrEmpty method.

1 if ( String . IsNullOrEmpty ( name )) 2 { 3 ... 4 }

This method returns true if the specified string is null or empty, meaning that it contains no characters. But for this version, we’re going to make use of the IsNullOrWhiteSpace because we want to make sure that the user hasn’t typed in a few spaces and hit Enter.

Our final code will look like this:

1 using System ; 2 using static System . Console ; 3 4 namespace string_formatting_tips 5 { 6 class Program 7 { 8 static void Main ( string [] args ) 9 { 10 while ( true ) 11 { 12 WriteLine ( "Enter your name:" ); 13 14 string name = ReadLine (); 15 16 if ( String . IsNullOrWhiteSpace ( name )) 17 { 18 WriteLine ( "Please enter your name" ); 19 continue ; 20 } 21 22 WriteLine ( "Hello!" + name ); 23 break ; 24 } 25 26 ReadLine (); 27 } 28 } 29 }

String Formatting and String Interpolation

1 using System ; 2 using static System . Console ; 3 4 namespace string_formatting_tips 5 { 6 class Program 7 { 8 static void Main ( string [] args ) 9 { 10 var name = GetName (); 11 var age = GetAge (); 12 13 WriteLine ( "Name:" + name + " Age:" + age ); 14 ReadLine (); 15 } 16 17 public static string GetName () 18 { 19 while ( true ) 20 { 21 WriteLine ( "Enter your name:" ); 22 23 string name = ReadLine (); 24 25 26 if ( String . IsNullOrWhiteSpace ( name )) 27 { 28 WriteLine ( "Please enter your name" ); 29 continue ; 30 } 31 32 return name ; 33 } 34 } 35 36 public static int GetAge () 37 { 38 while ( true ) 39 { 40 WriteLine ( "Enter your age:" ); 41 42 string age = ReadLine (); 43 44 45 if ( String . IsNullOrWhiteSpace ( age )) 46 { 47 WriteLine ( "Please enter your age" ); 48 continue ; 49 } 50 51 return Convert . ToInt32 ( age ); 52 } 53 } 54 } 55 }

In the above program, we take name and age as input and display it using the string concatenation.

1 WriteLine ( "Name:" + name + " Age:" + age ); 2 ReadLine ();

But this is a little bit clunky. As an alternative, we can make use of the string.Format method.

There are several different overloads to this method, one of which allows us to provide a formatting string as the first parameter and then one or more arguments to fulfill that format.

1 var line = String . Format ( "Name:{0} Age:{1}" , name , age ); 2 WriteLine ( line );

Notice that we’ve got these braces or curly brackets, and inside these braces, we’ve got the values 0 and 1. We can then specify values that will be inserted into these positions, 0 and 1. So into position 0, we want to output the name, and into this second position, we want to insert the value age.

I’m just going to tidy this up so we can see it on one line.

1 WriteLine ( String . Format ( "Name:{0} Age:{1}" , name , age ));

And the benefit of this method is we’re not manually concatenating strings. We can see the overall message here, and then the values that get inserted into that message.

Some methods provide a convenient way to pass a format string and a number of values, and then under the hood call string.Format. So this applies to the WriteLine method. One of the overloads of the WriteLine method allows us to specify a format string and then a number of values like this:

1 WriteLine ( "Name:{0} Age:{1}" , name , age );

One disadvantage of the string.Format approach is that we’re somewhat disconnected between the items in the format string, such as 0 and 1, and the actual data that’s being passed into them.

An alternative method that solves this problem is to make use of C#’s string interpolation. So we can create an interpolated string by prefixing the string with a dollar sign, as we can see here.

1 WriteLine ( $ "Name:{name} Age:{age}" );

As with the string.Format version, we make use of the braces, but inside the braces here, notice that rather than specifying positions such as 0 and 1, we specify directly the item that we want to output.

So this may help prevent bugs where we’ve got the order of the string.Format values mixed up. For example, here if we accidentally mixed up the order of name and age, we wouldn’t get a compilation error and the program would still run, but we would get the wrong data item’s output.

Formatting and Aligning Values into Columns

Sometimes you may want to format strings in such a way as things line up perfectly in columns.

1 using System ; 2 using System.Collections.Generic ; 3 using static System . Console ; 4 5 namespace string_formatting_tips 6 { 7 class Program 8 { 9 static void Main ( string [] args ) 10 { 11 var people = new List < Person >() 12 { 13 new Person { Name = "Clarence Barnes" , Age = 27 }, 14 new Person { Name = "Erica Wood" , Age = 33 }, 15 new Person { Name = "Lois Watson" , Age = 56 }, 16 new Person { Name = "Teresa Moore" , Age = 25 }, 17 new Person { Name = "Addison Richardson" , Age = 45 } 18 }; 19 20 foreach ( var person in people ) 21 { 22 WriteLine ( "Name: " + person . Name + " Age:" + person . Age ); 23 } 24 25 ReadLine (); 26 } 27 28 public class Person 29 { 30 public string Name { get ; set ; } 31 public int Age { get ; set ; } 32 } 33 } 34 }

The above program generates the following result:

1 Name: Clarence Barnes Age:27 2 Name: Erica Wood Age:33 3 Name: Lois Watson Age:56 4 Name: Teresa Moore Age:25 5 Name: Addison Richardson Age:45

Let’s see how we can line up the output perfectly in columns.

We’ll start with a harder way of solving this problem. Once again, we’re going to use string concatenation.

1 WriteLine ( "Name: " + person . Name . PadRight ( 20 )+ " Age:" + person . Age . ToString (). PadRight ( 20 ));

Here we call the string’s PadRight method and pass in a value of 20, which represents the total column width that we want here. So as long as none of the first names exceed 20 we should be okay.

We continued with this string concatenation, Notice here, though, that we don’t have the PadRight method available on the Integer type, so we need to convert this to a string first.

So we called ToString.PadRight, and made the column holding the Age 20 wide as well.

The above changes will generate the following result:

1 Name: Clarence Barnes Age:27 2 Name: Erica Wood Age:33 3 Name: Lois Watson Age:56 4 Name: Teresa Moore Age:25 5 Name: Addison Richardson Age:45

There’s an easier way to accomplish this, however, and that’s to once again make use of the string.Format method.

1 WriteLine ( String . Format ( "Name: {0,-20} Age:{1,-20}" , person . Name , person . Age ));

or simply do this:

1 WriteLine ( "Name: {0,-20} Age:{1,-20}" , person . Name , person . Age );

or this:

1 WriteLine ( $ "Name: {person.Name,-20} Age:{person.Age,-20}" );

So this is almost identical to what we saw before, but notice in the formatting expressions here we’ve got an additional parameter. The first parameter specifies the position of the argument that will be mapped to that element, so 0 represents Name and 1 represents Age, but the second parameter after the comma allows us to specify formatting information.

Once again, we want a left‑aligned Name column 20 wide, so we need to specify ‑20 to get that left‑aligned. And for the Age, once again, left‑aligned, 20 wide.

In addition to using the second parameter of these items to specify column widths, we can also use them to specify formatting, for example to output numbers or dates. If you want to learn more about the different options available here, be sure to check out the documentation.

Conditional Formatting for Positive, Negative, and Zero Numbers

Let’s see a completely different example, suppose we want to display a product ranting of type int.

1 using System ; 2 using System.Collections.Generic ; 3 using static System . Console ; 4 5 namespace string_formatting_tips 6 { 7 class Program 8 { 9 static void Main ( string [] args ) 10 { 11 var products = new List < Product >() 12 { 13 new Product { Name = "BLACK SUN GLASS" , Rating = 5 }, 14 new Product { Name = "BEAUTIFUL HEAD CAP" , Rating = 3 }, 15 new Product { Name = "HEAD PROTECTED CAP" , Rating = 5 }, 16 new Product { Name = "BORING HEAD CAP" , Rating = - 5 }, 17 new Product { Name = "NEW AGE SUN GLASS" , Rating = 0 } 18 }; 19 20 foreach ( var product in products ) 21 { 22 WriteLine ( $ "Name: {product.Name,-20} Rating:{product.Rating,-20}" ); 23 } 24 25 ReadLine (); 26 } 27 28 public class Product 29 { 30 public string Name { get ; set ; } 31 public int Rating { get ; set ; } 32 } 33 } 34 } 35 36 /* 37 Output: 38 Name: BLACK SUN GLASS Rating:5 39 Name: BEAUTIFUL HEAD CAP Rating:3 40 Name: HEAD PROTECTED CAP Rating:5 41 Name: BORING HEAD CAP Rating:-5 42 Name: NEW AGE SUN GLASS Rating:0 43 */

Suppose that rather than outputting the integer value for the productivity rating as it is, we wanted some conditional formatting. So, for example, we wanted to output the text Good if the value is positive, we wanted to output Bad if the value is negative, and if the value is 0, we want to assume that they’re a new product and don’t currently have a rating.

So we could use an if statement or ternary operator to do this depending on the value for productivity rating,

1 var rating = product . Rating < 0 ? "Bad" : product . Rating > 0 ? "Good" : "Not Rated" ; 2 WriteLine ( $ "Name: {product.Name,-20} Rating:{rating,-20}" );

but there’s an easier way, and that’s to use a three‑part conditional formatting string.

1 WriteLine ( $ "Name: {product.Name,-20} Rating:{product.Rating.ToString(" Good ; Bad ; Not Rated "),-20}" );

A three‑part format string is separated by semicolons here. In the first part of this three‑part format, we can specify how we want positive numbers presented.

We can also output the actual value. One way we can get the actual value is to specify the hash or the pound symbol, and this will map to the numeric value, in our case the rating.

1 product . Rating . ToString ( "Good #;Bad -#;Not Rated" )

So now we’ve defined this format, we can go and make use of it:

1 using System ; 2 using System.Collections.Generic ; 3 using static System . Console ; 4 5 namespace string_formatting_tips 6 { 7 class Program 8 { 9 static void Main ( string [] args ) 10 { 11 var products = new List < Product >() 12 { 13 new Product { Name = "BLACK SUN GLASS" , Rating = 5 }, 14 new Product { Name = "BEAUTIFUL HEAD CAP" , Rating = 3 }, 15 new Product { Name = "HEAD PROTECTED CAP" , Rating = 5 }, 16 new Product { Name = "BORING HEAD CAP" , Rating = - 5 }, 17 new Product { Name = "NEW AGE SUN GLASS" , Rating = 0 } 18 }; 19 20 foreach ( var product in products ) 21 { 22 WriteLine ( $ "Name: {product.Name,-20} Rating:{product.Rating.ToString(" Good # ; Bad - # ; Not Rated "),-20}" ); 23 } 24 25 ReadLine (); 26 } 27 28 public class Product 29 { 30 public string Name { get ; set ; } 31 public int Rating { get ; set ; } 32 } 33 } 34 }

The above program will generate the following result:

1 Name: BLACK SUN GLASS Rating:Good 5 2 Name: BEAUTIFUL HEAD CAP Rating:Good 3 3 Name: HEAD PROTECTED CAP Rating:Good 5 4 Name: BORING HEAD CAP Rating:Bad -5 5 Name: NEW AGE SUN GLASS Rating:Not Rated

Building Strings with the StringBuilder Class

Let’s say we want to display all the above products name in one line separated by a comma.

1 BLACK SUN GLASS, BEAUTIFUL HEAD CAP, HEAD PROTECTED CAP, BORING HEAD CAP, NEW AGE SUN GLASS,

Then the required program might look like this,

1 var line = "" ; 2 foreach ( var product in products ) 3 { 4 line += product . Name + ", " ; 5 } 6 Console . WriteLine ( line );

So this first version uses string concatenation here where we’re just adding one string to another string inside this loop. When you’re concatenating strings like this, one alternative is to use the StringBuilder class.

1 var stringBuilder = new StringBuilder ();

The two most common methods that you’ll use are the StringBuilder.Append method and the AppendLine method, which adds a carriage return line feed at the end.

1 foreach ( var product in products ) 2 { 3 stringBuilder . Append ( product . Name ); 4 stringBuilder . Append ( ", " ); 5 }

To get the final string, we can call the ToString method of the StringBuilder. But because we’re using string interpolation here, we don’t have to manually specify the ToString method, it will be called automatically for us.

1 Console . WriteLine ( stringBuilder );

There are two main reasons why you might want to consider using a StringBuilder over manually concatenating strings.

The first is performance. In this example, we’ve only got four products in the list of products, so the foreach loop will only execute four times. In this first version using string concatenation, we probably won’t experience too much of a performance problem, but if we had 1000 items in the list or 10,000, or even more, this method of string concatenation is inefficient.

If you’re performing string concatenation inside of a loop, and you might perform the concatenation many, many times, you may want to consider instead using the StringBuilder version.

The second reason you may want to consider using a StringBuilder is if it improves readability. Even if you’re not concatenating strings inside a loop, you may feel that a StringBuilder offers a more readable syntax.

Creating and Using Custom Numeric Format Providers

Sometimes you might have more complex formatting requirements, or you may want to create the formatting once and then reuse it in multiple places or multiple applications. One way to achieve this is to create custom format providers. Let’s create one for the rating.

1 using System ; 2 using System.Collections.Generic ; 3 using System.Text ; 4 using static System . Console ; 5 6 namespace string_formatting_tips 7 { 8 class Program 9 { 10 static void Main ( string [] args ) 11 { 12 var products = new List < Product >() 13 { 14 new Product { Name = "BLACK SUN GLASS" , Rating = 5 }, 15 new Product { Name = "BEAUTIFUL HEAD CAP" , Rating = 3 }, 16 new Product { Name = "HEAD PROTECTED CAP" , Rating = 5 }, 17 new Product { Name = "BORING HEAD CAP" , Rating = - 5 }, 18 new Product { Name = "NEW AGE SUN GLASS" , Rating = 0 } 19 }; 20 21 foreach ( var product in products ) 22 { 23 var rating = String . Format ( new RatingFormatProvider (), "{0}" , product . Rating ); 24 WriteLine ( $ "Name: {product.Name,-20} Rating: {rating,-20}" ); 25 } 26 27 ReadLine (); 28 } 29 30 public class Product 31 { 32 public string Name { get ; set ; } 33 public int Rating { get ; set ; } 34 } 35 36 public class RatingFormatProvider : IFormatProvider , ICustomFormatter 37 { 38 public string Format ( string format , object arg , IFormatProvider formatProvider ) 39 { 40 int rating = ( int ) arg ; 41 42 return rating . ToString ( "Good #;Bad -#;Not Rated" ); 43 } 44 45 public object GetFormat ( Type formatType ) 46 { 47 if ( formatType == typeof ( ICustomFormatter )) 48 { 49 return this ; 50 } 51 52 return null ; 53 } 54 } 55 } 56 }

Summary

So that brings us to the end of this article. We started by learning how we can simplify null or whitespace checking code with one of the string static methods such as the string.IsNullOrWhiteSpace method.

Next, we started to look at different ways to format content, such as using the string.Format method, or instead, how we can make use of string interpolation to simplify the code.

Next, we learned some different ways to format values in two columns. And then we learned how to create a three‑part numeric conditional formatting string that allows us to specify different formatting for positive, negative, and 0 values.

We learned as an alternative to manually concatenating strings. We can instead use an instance of the StringBuilder class, and this may give us performance benefits if we’re doing string concatenation within a loop and we have a large number of loop iterations.

Finally, we learned how we can create custom formatting and centralize it in one location by creating a custom format provider. To do this, we need to implement the IFormatProvider and ICustomFormatter interfaces.

Further Reading