Datatypes and Operators

data types are provided in the System namespace. All of them, however, have a C# alias. These aliases are keywords in the C# language

The integral types

The default value of all integral types is 0. All of these types define two constants called MinValue and MaxValue, which provide the minimum and maximum value of the type.

Integral literals, which are numbers that appear directly in code (such as 0, -42, and so on), can be specified as decimal, hexadecimal, or binary literals. Decimal literals do not require any suffix. Hexadecimal literals are prefixed with 0x or 0X, and binary literals are prefixed with 0b or 0B. An underscore (_) can be used as a digit separator with all numeric literals.

int dec = 32;
int hex = 0x2A;
int bin = 0b_0010_1010;

An integral value without any suffix is inferred by the compiler as int. To indicate a long integer, use l or L for a signed 64-bit integer and ul or UL for an unsigned 64-bit integer.

The floating-point types

The default value for floating-point types is 0. These types also define two constants called MinValue and MaxValue that provide the minimum and maximum value of the type. However, these types also provide constants that represent not-a-number (System.Double.NaN) and infinity (System.Double.NegativeInfinity and System.Double.PositiveInfinity)

var a = 42.99;//double
float b = 19.50f;
System.Double c = -1.23;

The decimal type

The decimal type can represent up to 28 decimal places or 128 bits. is important to note that the decimal type minimizes errors during rounding but does not eliminate the need for rounding. For instance, the result of the operation 1m / 3 * 3 is not 1 but 0.9999999999999999999999999999. On the other hand, Math.Round(1m / 3 * 3) yields the value 1.

decimal a = 42.99m;
var b = 12.45m;
System.Decimal c = 100.75M;

The char type

16 bits

char a = 'A';
char b = '\x0065';
char c = '\u15FE';

The string type

A string is an array of characters. In C#, the type for representing a string is called string and is an alias for the .NET System.String. You can use any of these two types interchangeably. Internally, a string contains a read-only collection of char objects. This makes strings immutable, which means that you cannot change a string but need to create a new one every time you want to modify the content of an existing string. Strings are not null-terminated (unlike other languages such as C++) and can contain any number of null characters ('\0'). 

string s1;                       // unitialized
string s2 = null;                // initialized with null
string s3 = String.Empty;        // empty string
string s4 = "hello world";       // initialized with text
var s5 = "hello world";
System.String s6 = "hello world";
char[] letters = { 'h', 'e', 'l', 'l', 'o'};
string s7 = new string(letters); // from an array of chars
char c = s4[0];  // OK
s4[0] = 'H';     // error
var s8 = s6.Remove(5);       // hello
var s9 = s6.ToUpper();       // HELLO WORLD

formats

int i = 42;
string s1 = "This is item " + i.ToString();
string s2 = string.Format("This is item {0}", i);
string s3 = $"This is item {i}";
string s4 = @"\b\w+\b";

The object type

The object type is the base type for all other types in C#, even though you do not specify this explicitly, as we will see in the following chapters. The object keyword in C# is an alias for the .NET System.Object type. You can use these two interchangeably.

GetType() method, which is not virtual and which returns a System.Type object with information about the type of the current instance.

Equals()  behavior is different for reference and value types. for reference types, this method performs reference equality; this means it checks whether the two variables point to the same object on the heap. For value types, it performs value equality; this means that the two variables are of the same type and that the public and private fields of the two objects are equal.

The default value of a variable of the object type is null

Reference types and value types

value types All user-defined types declared as structures (with the struct keyword) are value types. Although all types are implicitly derived from the object, type value types do not support explicit inheritance
Value types are typically stored on the stack in memory

int a = 20;
DateTime dt = new DateTime(2019, 12, 25);
int a = 20;
int b = a;  // b is 20
a = 42;     // a is 42, b is 20

reference type does not contain the value directly but a reference to a memory location where the actual value is stored. The built-in data types object and string are reference types. Arrays, interfaces, delegates, and any user-defined type defined as a class are also called reference types

Nullable types

Reference types have the default value null, which indicates that a variable is not assigned to the instance of any object. Value types do not have such an option. However, there are cases when no value is a valid value for a value type too. To represent such cases, you can use a nullable type.
A nullable type is an instance of System.Nullable, a generic value type that can represent the values of an underlying T type, which can only be a value type, as well as an additional null value.

Nullable<int> a;
Nullable<int> b = null;
Nullable<int> c = 42;
int? a;
int? b = null;
int? c = 42;

You can use the HasValue property to check whether a nullable type object has a value, and Value to access the underlying value:

if (c.HasValue)
    Console.WriteLine(c.Value);
string? s1 = null; // OK, nullable type
string s2 = null;  // error, non-nullable type
  • You assign values to a nullable type object the same way you would assign to the underlying type.
  • You can use the GetValueOrDefault() method to get either the assigned value or the default value of the underlying type if no value is assigned.
  • Boxing is performed on the underlying type. If the nullable type object has not assigned any value, the result of boxing is a null object.
  • You can use the null-coalescing operator, ??, to access the value of the object of a nullable type (for example, int d = c ?? -1;).

Array

int[] arr1;
int[] arr2 = null;
int[] arr3 = new int[6];
int[] arr4 = new int[] { 1, 1, 2, 3, 5, 8 };
int[] arr5 = new int[6] { 1, 1, 2, 3, 5, 8 };
int[] arr6 = { 1, 1, 2, 3, 5, 8 };
foreach(int element in arr6)
 Console.WriteLine(element);
int[,] arr1;
arr1 = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr2 = null;
int[,] arr3 = new int[2,3];
int[,] arr4 = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr5 = new int[2,3] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr6 = { { 1, 2, 3 }, { 4, 5, 6 } };
int[][] arr1;
int[][] arr2 = null;
int[][] arr3 = new int[2][];
arr3[0] = new int[3];
arr3[1] = new int[] { 1, 1, 2, 3, 5, 8 };
int[][] arr4 = new int[][]
{
   new int[] { 1, 2, 3 },
   new int[] { 1, 1, 2, 3, 5, 8 }
};
int[][] arr5 =
{
   new int[] { 1, 2, 3 },
   new int[] { 1, 1, 2, 3, 5, 8 }
};
int[][,] arr6 = new int[][,]
{
    new int[,] { { 1, 2}, { 3, 4 } },
    new int[,] { {11, 12, 13}, { 14, 15, 16} }
};

Type Conversion

string s = "example";
object o = s;          // implicit conversion
string r = (string)o;  // explicit conversion
public readonly struct fancyint
{
    private readonly int value;
    public fancyint(int value)
    {
        this.value = value;
    }
    public static implicit operator int(fancyint v) => v.value;
    public static explicit operator fancyint(int v) => new fancyint(v);
    public override string ToString() => $"{value}";
}
fancyint a = new fancyint(42);
int i = a;                 // implicit conversion
fancyint b = (fancyint)i;  // explicit conversion
DateTime dt1 = DateTime.Parse("2019.08.31");
DateTime.TryParse("2019.08.31", out DateTime dt2);
int i1 = int.Parse("42");          // successful, i1 = 42
int i2 = int.Parse("42.15");       // error, throws exception
int.TryParse("42.15", out int i3); // error, returns false, 
                                   // i3 = 0

Operators

The null-conditional operators

The null-conditional operator has two forms: ?. (also known as the Elvis operator) to apply member access and ?[] to apply element access for an array. These operators apply the operation to their operand if and only if that operand is not null. Otherwise, the result of applying the operator is also null

if(a is null)
   a = b;
//or
a??=b;