The declaration and initialization of variables are fundamental to understanding any language. C# is a type-safe language, which means that all variables and constants in C# must have a type, and the declared type is immutable. In C#, types are either reference or value type. For further information on this, please see this article.
Variables can either be initialized in the same statement as the declaration or later in the code. This article discusses different ways of declaring and initializing variables, as well as when to use each way.
Local variables are variables declared inside a code block, which is usually part of a method, property, or delegate. Declarations specify the type followed by the name, and optionally the initialization. Initialization sets the variable to a new instance. It must be to a type that is compatible with the declaration type.
1static void Main(string[] args)
2{
3 string a = "Hello World";
4 Console.WriteLine(a);
5}
In the above code, the variable a
is declared as a string and is initialized to "Hello World". The declaration below achieves precisely the same result.
1var a = "Hello World";
The var
keyword specifies implicit typing. The code does not specify the type, and the compiler can infer the type from the initialization. The compiler recognizes that the literal inside the double quotes is a string and declares the variable as such. Implicitly typed variables should not be confused with variant types. var
maintains C#'s type-safety mechanisms and does not allow a type change after declaration.
Explicitly typed variables can add readability to the code when it is not clear what type the compiler infers. Implicitly typed variables are less verbose and require fewer changes when refactoring. Teams should create guidelines around when to use explicit and implicit type declarations.
The code must initialize local variables before usage. This code causes a compilation error.
Use of unassigned local variable 'a'
1static void Main(string[] args)
2{
3 string a;
4 Console.WriteLine(a);
5}
Initialize the variable a
to its default value of null
or default
to fix this issue.
1static void Main(string[] args)
2{
3 string a = null;
4 Console.WriteLine(a);
5}
Local variables have a maximum scope of the method that contains them. It means that code outside the method cannot use the declaration. For further information on the scope of variables, please see this article.
Fields are member variables that belong to a type. They can be specified with modifiers such as public
or private
to specify access. static
fields only have one instance for the lifespan of the application. Types declare one variable instance for each non-static field on the type instantiated. Unlike local variables, fields require explicit type declaration, so the code cannot use var
. Local variables should be converted to fields when more than one method or property needs to use the variable.
Fields are generally used to store data that is private to the type and not exposed via the public interface. It is possible to declare a field as public
, but it is generally not considered best practice. Properties are generally used to expose variables publicly.
Unlike local variables, fields allow usage before initialization with code. This code does not cause a compilation or runtime error.
1public class Messenger
2{
3 private DateTime _currentDate;
4
5 public void SendMessage()
6 {
7 Console.WriteLine(_currentDate);
8
9 _currentDate = DateTime.Now.Date;
10 Console.WriteLine(_currentDate);
11 }
12}
Output:
1/1/0001 12:00:00 AM
11/23/2019 12:00:00 AM
Notice that currentDate
defaults to DateTime.MinValue
. After the first WriteLine
call, the code sets it to a new instance, which represents today's current date. Code can initialize fields before construction next to the declaration, like so.
1public class Messenger
2{
3 private DateTime _currentDate = DateTime.Now.Date;
4}
Object initializers offer a shorthand way of initializing a type's member variables. Consider this class.
1public class Values
2{
3 public string A { get; set; }
4 public string B { get; set; }
5}
This code creates an instance of Values
and initializes the two properties.
1class Program
2{
3 static void Main(string[] args)
4 {
5 var values = new Values();
6 values.A = "A";
7 values.B = "B";
8 }
9}
This can be shortened like so:
1class Program
2{
3 static void Main(string[] args)
4 {
5 var values = new Values { A = "A", B = "B" };
6 }
7}
This reduces the verbosity of the code and often makes it more readable.
Use caution when initializing values from methods or properties that may throw an exception. The values
property in this code never changes to a reference other than null
.
1class Program
2{
3 static void Main(string[] args)
4 {
5 Values values = null;
6
7 try
8 {
9 values = new Values
10 {
11 A = ThrowException(),
12 B = "B"
13 };
14 }
15 catch
16 {
17 }
18
19 Console.WriteLine($"values is null {values == null}");
20 Console.ReadLine();
21 }
22
23
24 public static string ThrowException()
25 {
26 throw new Exception();
27 }
28}
Output:
values is null True
Initialize collections and arrays with collection initializers. This code creates a list of integers with three elements.
1static void Main(string[] args)
2{
3 var numbers = new List<int> { 1, 2, 3 };
4
5 foreach (var number in numbers)
6 {
7 Console.WriteLine(number);
8 }
9
10 Console.ReadLine();
11}
Output:
1
2
3
Anonymous types are types without a name. Declare them in the scope of a code block. They can be useful for specific tasks like dynamically building an object to serialize to JSON and sending it to a RESTful service.
1class Program
2{
3 static void Main(string[] args)
4 {
5 var values = new { A = "A", B = "B" };
6 Console.WriteLine(values.A);
7 Console.WriteLine(values.B);
8 Console.ReadLine();
9 }
10}
Output:
A
B
Anonymous types are type-safe. The code cannot modify declared members after declaration. Use anonymous types sparingly. C# works best with explicit type naming, and anonymous types often become confusing for the reader.
Variables always have a type in C#. Type-safety is fundamental to the language and separates it from languages like JavaScript. Developers should pay attention to the differences between value and reference types. They should make decisions about when to use explicit vs. implicit types, and use object and collection initializers where appropriate.