An important distinction that is often unmentioned when talking about C# is that most of the objects and types used are not native to C# itself. They are defined in the assemblies that are part of the .NET Framework. They’re no different than the types you or I can create. But a few of these types are special. These are the “built in types,” often called “primitive types,” though that is a misnomer. All primitive data types in C# are objects in the System namespace. For each data type, alias (a short name) is provided. For instance, int is the short name for System.Int32 and double is the short form of System.Double. In some languages, like C, these types would actually be defined as part of the language. In C# though, they are simply aliases to .NET defined types. That’s why they’re not properly “primitives,” since they’re actually hiding standard classes and structs.
Why is this important to learn about?
These basic types are the most important of all to learn, because they are the foundation of everything you will program. These types are the most basic expression of data. If you don’t understand how they work from the beginning, anything you build can be flawed.
Every programming language has its own set of basic, built-in data types using which and object oriented approach we can build more complex data types such as classes & structures. C# has its own set of basic, fundamental types. Basic built-in types in C# are stored under System namespace, they are called primitive types. Since primitive types are used quite often, C# has a simplified way to create them. Instead of writing code like this:
System.Double x = new System.Double();
We can use a simpler way:
double x = 0;
All primitive types can be conditionally divided to the following types:
- Integral types:
- Real float-point types:
- Real type with decimal precision:
- Boolean type:
- Character type:
- String type:
- Object type:
C Sharp Data Types Table
int x = 0; float y = 0.0F; bool t = false; decimal d = 0.0M;
Implicit typing with ‘var’ keyword - compiler to infer the type of the variable from the expression on the right side of the initialization statement.
var is not part of language, it’s a visual studio feature.
var x = 10; // int var x = true; // bool var x = "Hassan"; // string
Implicitly typed variables must be initialized with proper value
var str = null // invalid, compiler can't figure out type of str var str = (string) null; // compiler can infer type of str as string, and assigned value is null
Everything is Object
For each primitive data type, there is a wrapper class that represents it as an object. For example,
System.Int32 class wraps the
int data type, and
System.Double class wraps the
double data type.
int x = 0 int x = new int() // same as int x = 0 System.Int32 x = new System.Int32(); // same as int x = 0
All primitive types besides string are derived from System.ValueType class which in its turn is derived from System.Object. It means we can call methods of System.Object on any primitive type and all primitive types are derived from System.Object that fits .NET methodology. For example, calling GetType, you may always get FCL type that corresponds to short name. Also, can typeof operator be used for getting type of the variable. Children of ValueType are automatically stored in stack when we declare them. Basing on it, they are very fast and effective.
Boxing and unboxing
The process of converting a value type to a reference type is called boxing. The inverse process - converting a reference type to a value type, is called unboxing.
int i = 123; // i is value type object o = i; // boxing int j = (int)o; // unboxing
In relation to simple assignments, boxing and unboxing are computationally expensive processes. When a value type is boxed, a new object must be allocated and constructed. To a lesser degree, the cast required for unboxing is also expensive computationally.
One of the benefits of OOP is value type variables are initialized (when created) by default constructor (since everything is 0bject, there is constructor). The following table shows the default values of value types returned by the default constructors. Default constructors are invoked by using the new operator, as follows:
int x = new int();
The above statement is the same as:
int x = 0;
C Sharp Default Value Table
C# allows both small and capital (use capital)
decial m = 0M; double d = 0.0D; float f = 0.0F; long l = 0L; uint uin = 0U; ulong ul = 0UL;
C Sharp Type Suffix Table