C# Data Types

Elucian MoiseElucian Moise
7 min read

Design

C# is a statically typed language, which means that variables are associated with a specific data type from compile time itself. The main advantage of this is:

  • Early error detection: Since the type of a variable is known at compile time, the compiler can detect type mismatches and other type-related errors during compilation itself. This helps catch errors early on.

  • Performance: Statically typed languages typically perform better since the compiler can perform certain type-related optimizations.

  • Code clarity: Having explicit types for variables makes the code more self-documenting and easier to understand and maintain.

An example of static typing in C#:

int number = 10;  // Variable number is of type int
string text = "Hello"; // Variable text is of type string

// This will result in a compiler error 
number = "Ten";

Here, the compiler knows that number is of type int and can detect that assigning a string value to it is a type mismatch, resulting in an error at compile time itself.

So in summary, static typing helps catch errors early, improves performance and makes code more readable - all of which are advantages for large codebases.


Overview

Every variable in C# has a data type associated with it. Data types define:

  • The type of values that can be stored

  • The operations that can be performed

  • The size and range of the memory allocated

C# supports a variety of built-in data types to cover most programming needs. The main categories are:

Value Types

Value types store the actual data within the variable. Some main value types are:

Integral Types

  • int - Stores whole numbers, range from -2,147,483,648 to 2,147,483,647

  • short - Stores short integers, range from -32,768 to 32,767

  • long - Stores long integers, range from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

  • byte - Stores unsigned integers, range from 0 to 255

  • sbyte - Stores signed byte, range from -128 to 127

Floating Point Types

  • float - Stores real numbers, range from 1.5 x 10^-45 to 3.4 x 10^38

  • double - Stores real numbers with higher precision, range from 5.0 x 10^-324 to 1.7 x 10^308

Boolean Type

  • bool - Stores true or false values

Character Type

  • char - Stores a single character, range from '\u0000' to '\uFFFF' in Unicode

Reference Types

Reference types store a reference to the actual data. Some main reference types are:

  • String

  • Arrays

  • Class types

  • Interface types

It is important to start learning C# with data types. The purpose of the language is to manipulate data. Knowing how to define data and handle data is essential for understanding future examples of code, data structures and algorithms.


Use-Cases

Data types are used for variables, constants, and parameters in C#:

Variables

Variables are used to store values that can change during the execution of a program. They are declared using the datatype followed by the variable name:

int number;  
string name;
bool isActive;

The syntax for initializing a variable is:

datatype variablename = value;

int number = 10;  
string name = "John";
bool isActive = true;

Constants

Constants are used to store values that don't change. They are declared using the const keyword:

const double pi = 3.14;
const string greeting = "Hello";

The value of a constant cannot be changed once initialized.

Parameters

Parameters are used to pass values to methods and functions. They are defined inside the parentheses of a method declaration:

void PrintName(string name) {
   Console.WriteLine(name);
}

int AddNumbers(int num1, int num2) {
   return num1 + num2;
}

Here string name and int num1, int num2 are parameters of the methods, with their respective data types.

Parameters allow methods to be generalized so that they can operate on different inputs.

So in summary:

  • Variables are used to store changing values, declared using datatype variable name

  • Constants are used to store fixed values, declared using const datatype variable name

  • Parameters are used to pass inputs to methods, defined within the method parentheses


Type Inference

Yes, C# has type inference using the 'var' keyword. This allows the compiler to determine the type instead of explicitly specifying it.

For example:

int number = 10;  // Explicit typing

var number = 10; // Type inferred as int

Here the compiler infers that the type of 'number' is 'int' based on the initialization value of 10.

Type inference can make code more concise and reduce repetition, especially with complex types. For example:

List<string> names = new List<string> { "John", "Jane", "Jack" };

var names = new List<string> { "John", "Jane", "Jack" }; // Type inferred as List<string>

Here we don't have to repeat 'List' twice. The 'var' keyword lets the compiler infer the type.

However, type inference has some limitations in C#:

  • It can only be used with local variables, not fields, properties, methods, etc.

  • It cannot be used to specify the return type of a method.

So in summary, type inference with 'var' in C# allows the compiler to infer the type based on the initialization value. It helps reduce repetition and make code more concise, though it has some limitations in C#.


Variable Scope

The scope of a variable determines where it can be accessed in the code. Variables can have either local scope or global scope.

Local Scope

Variables declared within a method, block or if statement have local scope. This means they can only be accessed within that block of code.

For example:

void Method1() {
    int number = 10; // Local variable
    // number can be accessed here
}

// number cannot be accessed here - out of scope

Here number has local scope - it can only be accessed within Method1().

Local variables are created when the code block is entered and destroyed when it is exited.

Global Scope

Variables declared outside of any method have global scope. This means they can be accessed anywhere within the class.

For example:

int number;  // Global variable

void Method1() {
    number = 10; // Can access global variable 
}

void Method2() {
    number = 20; // Also accessible here
}

Here number has global scope - it can be accessed within any method in the class.

Global variables exist for the lifetime of the program.

Best Practices

It is generally considered best practice to:

  • Use local scope wherever possible
    This avoids namespace pollution and potential errors from unintended access.

  • Declare variables as close as possible to where they are used This makes the code more readable and maintainable.

  • Avoid global variables unless absolutely necessary They make the code harder to understand, debug and maintain.

So in summary, the scope of a variable determines its lifetime and accessibility. Global variables have wider scope while local variables have narrower scope. Following best practices regarding variable scope helps create cleaner, more organized code.


Context

Variable context in C# refers to the scope in which a variable is defined. Variables can have either local scope or global scope, as we discussed earlier.

Closures in C# allow accessing variables that are out of the immediate scope. A closure is a function object that has access to variables from its enclosing scope, even after the scope has closed.

For example:

int num = 10;  

Action a = () => {
   Console.WriteLine(num);  
};

num = 20;

a();  // Prints 20

Here the variable num is defined outside the scope of the lambda expression. But the lambda expression (which is a closure) still has access to num and can access its updated value, even though the scope where num is defined has closed.

C# supports closures using lambda expressions, anonymous methods and local functions. This allows defining functions that capture variable context from an outer scope.

For example, all of these are closures in C#:

  • Lambda expression: () => { ... }

  • Anonymous method: delegate { ... }

  • Local function:

void OuterFunction() {
   int num = 10;

   void LocalFunction() {
      Console.WriteLine(num);  
   }

   LocalFunction();
}

In summary, C# supports:

  • Variable context using local and global scope

  • Closures using lambda expressions, anonymous methods and local functions, which can access variables from outer scopes.

This allows defining functions that capture the context in which they were defined, even after that context has closed.


Disclaim: This article was created by AI. There are many new concepts in this article that may not make sense for you right now. Do not be warry. We will explain in details all the aspects in future articles.

0
Subscribe to my newsletter

Read articles from Elucian Moise directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Elucian Moise
Elucian Moise

Software engineer instructor, software developer and community leader. Computer enthusiast and experienced programmer. Born in Romania, living in US.