In this article, we’re going to tackle the popular question among many developers, and that’s “What’s the difference between string and String” and “When should I use string, and when should I use String” in my applications.
Both of these versions exist for a reason, so let’s see what they are and if they’re indeed that important.
Let’s dive in.
What Is String, and What Is String?
String
or rather System.String
is a class and it’s used to represent a sequence of UTF-16 code units or rather System.Char.
Simply put it represents text. It’s a class like any other and it inherits from System.Object
. It’s also a .NET (Framework or Core) type and its immutable by its nature which means it cannot be changed after it’s created. If you change it, you practically create and return another string upon modification of the existing string. That’s where StringBuilder comes in.
On the other hand, string
is a C# reserved keyword and an alias for String
. That means you can do something like this:
string foo = "hi"; String bar = "there!";
And this will be okay with the compiler, no problems whatsoever. It’s a nice feature and most of the time we don’t think too much about which one we use.
That brings us to the next point.
Are They the Same Thing?
But are they really the same thing though?
The answer is yes and no. What do we mean by that?
While they are the same and they compile the same way to the IL (Intermediate Language):
IL_0001: ldstr "hi" IL_0006: stloc.0 IL_0007: ldstr "there!" IL_000c: stloc.1
There are some differences to think about when using them. String
can be anything because it’s not a reserved keyword. It can be used as a variable name for example. It’s not forbidden, but we’ll see what the cost of that can be. string
on the other hand, cannot be used like that.
Other Aliases in C#
Beside string, there are several other aliases in C#:
- object: System.Object
- bool: System.Boolean
- byte: System.Byte
- sbyte: System.SByte
- short: System.Int16
- ushort: System.UInt16
- int: System.Int32
- uint: System.UInt32
- long: System.Int64
- ulong: System.UInt64
- float: System.Single
- double: System.Double
- decimal: System.Decimal
- char: System.Char
Mind you that not all of these are classes. Most of the types are structs and point to value types, with the exception of System.Object
and System.String.
What Are the Differences Between String and String in Practice?
To understand the differences even better, let’s go through some examples.
Example 1: variable names
var String = DateTime.Now; // Compiles perfectly var string = DateTime.Now; // Does not compile
String
can be used as a variable name, string
cannot.
There is one exception to this rule and that’s by using a special character @ (verbatim identifier) as a prefix to indicate that the keyword can be used as an identifier:
var @string = DateTime.Now; // Compiles
As for the reason why you would do this ever, we have no idea. If you do, let us know in the comments section.
Example 2: nameof and typeof
Aliases, including string
, can’t be used as a parameter for the nameof
expression, because they are not types themselves.
var foo = nameof(string); // Syntax error var bar = nameof(String); // Compiles normally
The typeof
operator, on the other hand, is okay with either:
var foo = typeof(string); // Okay var bar = typeof(String); // Okay
In both cases, the result is the same and that’s System.String
.
Example 3: underlying enum types
You might find this one while Google-ing, but it’s actually not true. It’s said that the underlying enum type must be declared as an alias:
public enum Foo : UInt32 { } // Invalid supposedly public enum Bar : uint { } // Valid
This is not true, it both compiles, and the enum types are called as usual.
Example 4: context matters
Take a look at this example:
string foo = 20; String bar = 20;
What would you say the result of this code is? If you guessed syntax error in both cases, you were right.
But, that’s not the whole story.
If we add something like this:
using String = System.Int32;
It makes the syntax error on the second line disappear.
Why?
Because the compiler now treats the String
type as an Int32 type. This just goes to show that string
and String
are not entirely the same.
Example 5: aliases as generic types
We can’t use a keyword as a generic type parameter, but we can name it String
though:
public class GenericList<String> // Compiler agrees it's okay, but rolls its eyes { } // or public class GenericList<string> // No! { }
This is certainly not recommended, but it’s another example of how different these are.
Example 6: String as a class name
String
can be a class name:
class MyClass { protected class String { } void Example() { var foo = String.Format("Hello, today is {0}.", DateTime.Today.DayOfWeek); String bar = "Goodbye"; } }
There are some serious consequences of using it like that though. Both lines in the Example
method are going to report a syntax error because now, String
is just a class inside the MyClass
. It’s not String
anymore.
Example 7: String as an identifier
Same goes for using String as a variable name:
class MyClass<TElement> { public IEnumerable<TElement> String { get; set; } void Example() { var foo = String.Format("Hello, today is {0}.", DateTime.Today.DayOfWeek); } }
This inevitably results in a syntax error.
Enough examples? Let’s get to the important point.
Which One Should I Use?
There is a general consensus that string
should be used when defining variable types, and that String
should be used to call the String
class methods and fields:
string foo = "Hi there"; var bar = String.Format("{0}, today is {0}.", foo, DateTime.Today.DayOfWeek);
We would go a bit further than that to say that if you work in a corporate environment, or in a team of developers, you should choose consistency over this style. Sometimes it’s more important to be consistent than to follow the unwritten rules that someone else has come up with.
But, Is It Just the Matter Style Though?
We’ve come to the most important part. Is this just a question of code style?
We’ve gone through some of the examples (Examples 4, 5, 6, 7) where we’ve seen that using String
in certain scenarios can lead to potential problems, and there are some things that simply cannot be done with a keyword itself.
In terms of safety, it’s safer to use string
over String
, since compiler helps us avoid almost all problematic scenarios.
On the other hand, String
is used in many valid scenarios like reflection, lexers, serialization, protocols… And for such libraries, string vs String might be an important issue.
We hope this clears it up a bit.
Conclusion
In this guide, we’ve reviewed the differences between String and string, and we’ve shown a few examples to demonstrate these differences.
If you have some interesting examples of string vs String yourself, we would like to hear from you in the comments section.