Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C#Lectures - Lecture 1: Primitive Types

0.00/5 (No votes)
26 Sep 2016 1  
I'm starting a set of C# lectures for our engineers. The first lecture is about C# built-in types also known as primitive types.

Full Lectures Set


Introduction

This is the first article from the set of C# lectures that I'm doing in my company for our engineers. It is about C# built-in types.

Primitive Types Basics

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 and structures. C# has its own set of basic, fundamental types. These types are value types which means that when we pass this type as argument to function, they are copied. In this article, I want to show some important and interesting things about value types.

There are 2 sorts of value types:

  • Built in value types
  • User defined value types

Basic built-in types in C# are stored under System namespace, they are called primitive types. Because primitive types are used quite often, C# has a simplified way to create them. Instead of writing code like this:

System.Double doubleDigit = new System.Double();

We can use a simpler way:

double doubleDigit = 0;

Actually, we have several ways to define variables of primitive types in C# and it is up to the developer to decide which one to use:

int i =0;
int j = new int();
System.Int32 k = 0;
System.Int32 j = new System.Int32();

Local variables of primitive types must be initialized before they are used.

In both cases, IL compiler will generate the same IL code when you build your project. All C# primitive types have analogs in .NET Framework Class Library. For example, int in C# corresponds to System.Int32 in FCL. The table below shows C# types and their representatives in FCL from MSDN:

Short Name .NET Class Type Width Range (bits)
byte Byte Unsigned integer 8 0 to 255
sbyte SByte Signed integer 8 -128 to 127
int Int32 Signed integer 32 -2,147,483,648 to 2,147,483,647
uint UInt32 Unsigned integer 32 0 to 4294967295
short Int16 Signed integer 16 -32,768 to 32,767
ushort UInt16 Unsigned integer 16 0 to 65535
long Int64 Signed integer 64 -9223372036854775808 to 9223372036854775807
ulong UInt64 Unsigned integer 64 0 to 18446744073709551615
float Single Single-precision floating point type 32 -3.402823e38 to 3.402823e38
double Double Double-precision floating point type 64 -1.79769313486232e308 to 1.79769313486232e308
char Char A single Unicode character 16 Unicode symbols used in text
bool Boolean Logical Boolean type 8 True or false
object Object Base type of all other types    
string String A sequence of characters    
decimal Decimal Precise fractional or integral type that can represent decimal numbers with 29 significant digits 128 ±1.0 × 10e−28 to ±7.9 × 10e28

Short names for us mean that in any of our code files, we have code like this: using byte = System.Byte. This is not actually that but you can assume it is. Some experienced developers as Jeffrey Richter recommend to use FCL types always instead of short names. For example, in C#, long is System.Int64, but other languages may use Int16 and Int32. Also, a lot of methods in FCL types have names that correspond to type names. For example: ReadBoolean, ReadInt32, etc.

All primitive types can be conditionally divided to the following types:

  • Integral types: sbyte, byte, short, ushort, int, uint, long, ulong
  • Real float-point types: float, double
  • Real type with decimal precision: decimal
  • Boolean type: bool
  • Character type: char
  • String type: string
  • Object type: object

We will concentrate our attention on the first five types and not discuss string and object in this article.

Boolean Type

This is the simplest of the primitive types - it can hold only two values: true or false. You can think about true as 1 and false as 0 if it is more convenient for you. If you convert bool to int, this is what you will get true will be 1 and false will be 0. Bool is the simplest but the most commonly used type in all applications it is used to control the flow of the program and you can see its use in all the conditional operators. Below is the sample code that demonstrates basic operations with bool type:

//declare boolean variable and assign default value for it
//you will see later that default value for bool is false
//so we could avoid assigning it to false and simply define it as bool bVar;
bool bVar = false;
bVar = true;
if (bVar)//here we use boolean value in conditional operator
{
    Console.WriteLine("We are here because our boolean value is true");
    bVar = false;
}
if (!bVar)
{
    Console.WriteLine("We are here because our boolean value is false");
}

Character Type

Char value is 16-bit numerical value. Compiler translates the numeric value to character literal. Unicode characters are used to present most languages of the world plus different specific character sets. Constants of the char can be written as character literals, hexadecimal escape sequence, Unicode representations or can be casted from input integer.

When I tried to assign to char value bigger than maximum, I got a compilation error:

But if this will happen in runtime and compiler can't control it, we will have Overflow exception in checked code and reset value otherwise. We will review overflow later in this article.

Following code at sixth cycle will assign value 0 to char variable:

//when we reach maximum value for char the next one will be 0
int i = char.MaxValue - 5;
for (int j = 0; j < 7; j++)
{
    cVar = (char)i;
    i++;
}

Integral Types

Variables of integral types contain integers and vary by the size of memory allocated for variable and the signed variable or not. Signed or not defines if variable of integer type can hold negative values. Most commonly used types of integers are int and long. Usually, if you need any integer value, you use int and if size of int doesn't fit your needs, you define long. Int is a 32 bit integer and long is a 64 bit integer. Any literal within int interval is considered as int, integers that are bigger than this are automatically treated as long. Longs that are in int range can be explicitly treated as longs by using "L" or "l" literal at the end:

long lVar = 1L;

In table with types characteristics and below in this article, we get ranges for each primitive type. To decide which one from integral types is needed for you, you may refer to these sections to take a decision.

Real Types

Real types in C# are the real numbers as they are known in mathematics. They are present by floating-point types of different range and precision. Range and precision is crucial to understand in floating-point types. Range defines the minimum and maximum values that type can hold. Precision defines the number of significant figures the variable holds.

float type also known as single precision real number. If you use characters "f" or "F" after digit literal, you explicitly point that the number is of type float. You should know that by default all real numbers are considered as double. Float has precision of seven significant digits. For example, digit 0.123456789 will be rounded to 0.1234568.

double type is known as double precision real number. Suffixes "d" and "D" are used after digit literal to point that number is of type double. The precision of double is form 15 to 16 significant digits . You should know that double has special values Double.PositiveInfinity and Double.NegativeInfinity.

decimal type is known as decimal floating-point arithmetic where numbers are presented in decimal system rather than binary one. This type doesn't lose accuracy when storing and processing floating-pointing numbers. Decimal type has precision from 28 to 29 significant digits . "m" character at the end of digit literal indicates that the number is of type decimal.

Types Characteristics

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.

Following fragment of code shows you some interesting characteristics of few value types in C#:

ushort ushortVar = new ushort();
Console.WriteLine("ushort type" +
                  " \n\t minimum value: " + ushort.MinValue +
                  " \n\t maximum value: " + ushort.MaxValue +
                  " \n\t default value for unassigned variable: " + ushortVar +
                  " \n\t FCL type: " + ushortVar.GetType() +
                  " \n\t size in bytes: " + sizeof(ushort));

long longVar = new long();
Console.WriteLine("long type" +
                  " \n\t minimum value: " + long.MinValue +
                  " \n\t maximum value: " + long.MaxValue +
                  " \n\t default value for unassigned variable: " + longVar +
                  " \n\t FCL type: " + longVar.GetType() +
                  " \n\t size in bytes: " + sizeof(long));

ulong ulongVar = new ulong();
Console.WriteLine("ulong type" +
                  " \n\t minimum value: " + ulong.MinValue +
                  " \n\t maximum value: " + ulong.MaxValue +
                  " \n\t default value for unassigned variable: " + ulongVar +
                  " \n\t FCL type: " + ulongVar.GetType() +
                  " \n\t size in bytes: " + sizeof(ulong));

float floatVar = new float();
Console.WriteLine("float type" +
                  " \n\t minimum value: " + float.MinValue +
                  " \n\t maximum value: " + float.MaxValue +
                  " \n\t default value for unassigned variable: " + floatVar +
                  " \n\t FCL type: " + floatVar.GetType() +
                  " \n\t size in bytes: " + sizeof(float));

Here is the result of the program above:

As you can see, we can see using method GetType of System.Object, we can read the FCL type of C# primitive type. Also using sizeof operator, we can read the size in bytes that is allocated for primitive type variable. Also, we get maximum and minimum values for types where it is possible.

(Full version of program, you can see in the attachment.)

Types Casting

There are two kinds of types casting between primitive types:

  • Implicit type casting - when compiler automatically casts one type to another. This happens when type casting is "safe" and there is no data loss. For example, casting from Int32 to Int64.
  • Explicit type casting - when developer takes a risk and explicitly sets the type to which we convert the value.

Samples:

short short_value = 10;
int int_value = short_value; //implicit casting
byte byte_value = (byte) short_value; //explicit casting

The rules how C# casts values with "not safe" cases, when the value can be bigger than maximum value of the type to which we cast are described in specification of C# under "Conversions". If you are not sure when doing explicit casting, I recommend you to write short test application with possible input values and try to cast them.

Overflow

When you work with primitive types, especially types that don't have big range for values such as short or byte, you may come to the very sensitive topic called overflow. If for example, byte variable has value equal to 200 and you add to it 60. What will happen? Maximum value for byte is 255, so we don't have place for 260 here. By default, C# doesn't react with exception to such a fact and simply adds 55 to original value and then when it reaches maximum value, we start from 0. In other words, 200 + 60 = 5. Basing on this fact, you should be very careful when working with such a thing as overflow, sometimes you can use it for your purposes and adopt your application for this, but sometimes this may be very efficiently hidden bug that is hard to catch. To avoid the overflow and get system reaction for it, you may use compiler option add.ovf which will generate System.OverflowException when you have it. Also you may use checked instruction for part of code and this code will also generate same exception. As you can see, developer has an option to control the overflow, but you should be familiar with tools that are available for you. See code snippet below that demonstrates the overflow:

byte v = 255;
            Console.WriteLine("Insert any positive value:");
            int c = Console.Read();
            //here we have value of byte that reached more than maximum value, but
            //the result is a new we didn't receive overflow exception since by default
            //compiler doesn't react to overflow for primitive types and 
            //not generate System.OverflowException
            //to add the check for oveflow you need to add "/checked+" to compiler command line
            byteVar = (byte)(v + c);
            Console.WriteLine("New byte is: " + byteVar);
            //we have also another way to make operation checked
            try 
            {
                byteVar = checked((byte)(v + c));
            }
            catch (OverflowException e) 
            {
                Console.WriteLine("Overflow exception catched: " + e.Message);
            }

You can use compiler option /checked+ and then system will control each operation of addition, subtraction, multiplication and type casting for overflow and raise OverflowException when it happens. If you use this option, the program will work slowly, but I recommend you to use this option at least for testing phase.

P.S. I'm missing string type here as my next lecture will be about text processing in C# and string will be described in detail there.

Listing

Full listing of small application that shows primitive types example is available as attachment.

Sources

  1. Jeffrey Richter - CLR via C#
  2. Andrew Troelsen - Pro C# 5.0 and the .NET 4.5 Framework
  3. MSDN
  4. http://www.introprogramming.info/english-intro-csharp-book/read-online/chapter-2-primitive-types-and-variables/
  5. http://www.dreamincode.net/forums/topic/217947-c%23-primitives-built-in-types

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here