Revision

Back to C# - Types


Arrays

  int[] array = new int[2]; // creates array of length 2, default values
  string[] array = new string[] { "A", "B" }; // creates populated array of length 2
  string[] array = { "A" , "B" }; // creates populated array of length 2
  string[] array = new[] { "A", "B" }; // creates populated array of length 2

  const int i = 1;
  string[] array = new string[i]; // OK only if i is a constant
  using System.Linq;

  int[] array = Enumerable.Repeat(1, 10).ToArray(); // creates array of length 10 populated with 1
  int[] array = Enumerable.Range(1, 10).ToArray(); // creates array from 1 to 10 (included)


Operations on Arrays

  int[] array = Enumerable.Range(1, 10).ToArray();


  /**** Basic Operations ****/
  int length = array.Length; // 10 - Size of array


  /**** Access, Slicing, Search and Insertion ****/
  int index = 3, indexStart = 2, indexEnd = 5, distanceFromEnd = 2; valSearch = 7, valInsert = 9;

  int val = array[index]; // Access - 4
  int val = array[^index]; // Access - Access element (array.Length - index) - 7
  int[] subArray = array[indexStart..indexEnd]; // Slicing - From indexStart to indexEnd - excluded) - [3, 4, 5]
  int[] subArray = array[indexStart..^distanceFromEnd]; // Slicing - From indexStart to index (array.Length - distanceFromEnd) included - [3, 4, 5, 6, 7, 8, 9]

  bool b = array.Contains(valSearch); // Contains - Can also use Array.Exists(array, x => x == valSearch) that requires a predicate (slower) - true
  int indexOf = Array.IndexOf(array, valSearch); // Search - Returns -1 if not found - 6

  array[index] = valInsert; // Insertion - Replace an existing value


  /**** Comparison Operations ****/
  int min = array.Min(); // 1 - Minimum of array - Note: Faster using for loop
  int max = array.Max(); // 10 - Maximum of array - Note: Faster using for loop


  /**** Aggregation Operations ****/
  int sum = array.Sum(); // 60 - Sum of array
  double average = array.Average(); // 6 - Average of array


  /**** Sort/Reverse Operations ****/
  Array.Sort(array); // Sort the array - 1 2 3 5 6 7 8 9 9 10
  Array.Reverse(array); // Reverse the array - 10 9 9 8 7 6 5 3 2 1


  /**** Copy Operations ****/
  int[] array2 = array; // Copy by reference (change to one modifies other)
  int[] array3 = (int[]) array.Clone(); // Copy by value (change to one does not modify other)


  /**** Print Operation ****/
  Console.WriteLine(String.Join("; ", array)); // 10; 9; 9; 8; 7; 6; 5; 3; 2; 1


Ressources


Multi Dimensional Arrays

  string[ , ] marray = new string[2, 3]; // Create an empty array with 2 rows and 3 columns
  int[ , ] marray = { { 1, 2, 3 }, { 3, 4, 5 } }; // Create an array with 2 rows and 3 columns
  int[ , ] marray = new int[2, 3]{ { 1, 2, 3 }, { 3, 4, 5 } }; // Create an array with 2 rows and 3 columns
  int[ , , ] marray = { { { 1, 3, 5 }, { 2, 4, 6 } }, { { 2, 4, 9 }, { 5, 7, 11 } } }; // 3 dimensional array


Operations on Multi Dimensional Arrays

  using System.Linq;

  int[ , ] marray = { { 1, 2, 3 }, { 3, 4, 5 } }; // Create an array with 2 rows and 3 columns


  /**** Basic Operations ****/
  Console.WriteLine(marray.GetLength(0)); // 2 - Number of Rows (1st dimension)
  Console.WriteLine(marray.GetLength(1)); // 3 - Number of Columns (2nd dimension)


  /**** Extract Columns and Rows ****/
  // Get Column of a marray using Linq
  public T[] GetColumn(T[,] marray, int columnNumber) {
      return Enumerable.Range(0, marray.GetLength(0))
              .Select(x => marray[x, columnNumber])
              .ToArray();
  }

  // Get Row of a marray using Linq
  public T[] GetRow(T[,] marray, int rowNumber) {
      return Enumerable.Range(0, marray.GetLength(1))
              .Select(x => marray[rowNumber, x])
              .ToArray();
  }


Jagged Arrays

A jagged array is an array whose elements are arrays, possibly of different sizes. A jagged array is sometimes called an “array of arrays.”. It can also be used with multi dimensional arrays.

  int[][] jaggedArray = new int[3][]; // Create an empty jagged array of 3 arrays of different undefined sizes
  jaggedArray[0] = new int[5]; // Set size of first array to 5
  jaggedArray[1] = new int[4]; // Set size of second array to 4
  jaggedArray[2] = new int[2]; // Set size of third array to 2

  int[][] jaggedArray = new int[3][]; // Create an empty jagged array of 3 arrays of different undefined sizes
  jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 }; // Define first array
  jaggedArray[1] = new int[] { 0, 2, 4, 6 }; // Define second array
  jaggedArray[2] = new int[] { 11, 22 }; // Define third array

  int[][] jaggedArray = new int[][]
  {
    new int[] { 1, 3, 5, 7, 9 },
    new int[] { 0, 2, 4, 6 },
  };

  int[][] jaggedArray =
  {
    new int[] { 1, 3, 5, 7, 9 },
    new int[] { 0, 2, 4, 6 },
  };


Operations on Jagged Arrays


List

C# List are Linked List (ie with variable size).
To use List, you need to import System.Collections.Generic.

  using System.Collections.Generic;

  List<int?> list = new List<int?>(); // creates an empty List
  List<int?> list = new List<int?>(){null, 2}; // creates populated List of length 2
  using System.Linq;

  List<int> list = Enumerable.Repeat(1, 10).ToList(); // creates a List of length 10 populated with 1
  List<int> list = Enumerable.Range(1, 10).ToList(); // creates List from 1 to 10 (included)


Operations on List

  using System.Collections.Generic;
  using System.Linq;


  List<int> list = Enumerable.Range(1, 10).ToList();


  /**** Basic Operations ****/
  int length = list.Count; // 10 - Size of List


  /**** Access, Slicing, Search, Insertion and Deletion ****/
  int index = 3, indexStart = 2, indexEnd = 5, valSearch = 7, valToInsertAdd = 9, valToInsertReplace = 6, nbElements = 4;

  int val = list[index]; // Access - 4
  List<int> subList = list.GetRange(indexStart, nbElements); // Slicing - nbElements starting at indexStart - [3, 4, 5, 6]

  bool b = list.Contains(valSearch); // Contains - true
  int indexOf = list.IndexOf(valSearch); // Search - Returns -1 if not found - 6

  list.Add(valToInsertAdd); // Insertion - Add a new value
  list[index] = valToInsertReplace; // Insertion - Replace an existing value
  list.InsertRange(indexStart, subList); // Insertion - Insert the list subList at index indexStart

  list.Remove(valSearch); // Remove - Remove first occurence
  list.RemoveAt(index); // Remove by index
  list.RemoveRange(indexStart, nbElements);// Remove Range - Remove nbElements elements starting at indexStart


  /**** Comparison Operations ****/
  int min = list.Min(); // 1 - Minimum of List
  int max = list.Max(); // 10 - Maximum of List


  /**** Aggregation Operations ****/
  int sum = list.Sum(); // 56 - Sum of List
  double average = list.Average(); // 6.22 - Average of List


  /**** Sort/Reverse Operations ****/
  list.Sort(); // Sort the list - 1 2 5 6 6 8 9 9 10
  list.Reverse(); // Reverse the list - 10 9 9 8 6 6 5 2 1


  /**** Copy Operations ****/
  /** Shallow Copy **/
  List<int> listCopy = new List<int>(list); // For value types List, change to one list does not modify the other (not true for reference types list)

  /** Deep Copy **/
  List<ReferenceType> listDeepCopy = new List<ReferenceType>(list.Count);
  list.ForEach((item) => { listDeepCopy.Add(new ReferenceType(item)); }); // Requires a copy constructor for ReferenceType


  /**** Print Operation ****/
  Console.WriteLine(String.Join("; ", list)); // 10; 9; 9; 8; 6; 6; 5; 2; 1


SortedList

To use SortedList (SortedList<TKey, TValue>), you need to import System.Collections.Generic.
SortedList<TKey, TValue> is a collection class that can store key-value pairs that are sorted by the keys based on the associated IComparer implementation (see Tutorial Teacher on Sorted Lists)

  using System.Collections.Generic;

  SortedList<int, string> sortedList = new SortedList<int, string>();
  sortedList.Add(3, "Three");
  sortedList.Add(1, "One");
  sortedList.Add(2, "Two");

  SortedList<int, string> sortedList = new SortedList<int, string>()
                                                {
                                                  {3, "Three"},
                                                  {5, "Five"},
                                                  {1, "One"}
                                                };


Operations on SortedList


Difference with Heap

See this Geeks for Geeks page. A Heap is stored as a Binary tree where the parents are always greater (MaxHeap) or lower (MinHeap) than their children. A Heap does not have rules for left and right children (it is not a binary search tree).

Time Complexity comparison between a MinHeap and a SortedList (ascending order):

Operation SortedList Heap
Insert O(n) O(log(n))
Search O(log(n)) O(n)
Find Min O(1) O(1)
Delete Min O(n) O(log(n))


HashSet

To use HashSet, you need to import System.Collections.Generic.

  using System.Collections.Generic;

  HashSet<int> hashset = new HashSet<int>();
  HashSet<int> hashset = new HashSet<int>() { 1, 2, 3 };


Operations on HashSet

  using System.Collections.Generic;
  using System.Linq;

  HashSet<int> hashset = new HashSet<int>() { 1, 2, 3 };


  /**** Basic Operations ****/
  int size = hashset.Count; // Get number of elements of hashset - Can use .Count()

  int val = 4, valAlreadyInHashet = 1;
  hashset.Add(val); // Add val to hashset
  hashset.Add(valAlreadyInHashet); // If val already in hashset, do Nothing

  bool b = hashset.Contains(val); // Check if key already in hashset


  /**** Extract Values ****/
  int[] arrayValues = hashset.ToArray(); // Requires Systel.Linq
  List<int> listValues = hashset.ToList(); // Requires Systel.Linq


  /**** Comparison Operations ****/
  int min = hashset.Min();
  var max = hashset.Max();


  /**** Intersection and Union Operations ****/
  HashSet<int> hashset2 = new HashSet<int>() { 1, 7, 8, 9 };
  HashSet<int> hashsetOutIntersection = hashset.Intersect(hashset2).ToHashSet(); // { 1 } - Requires System.Linq
  HashSet<int> hashsetOutUnion = hashset.Union(hashset2).ToHashSet(); // { 1, 2, 3, 4, 7, 8, 9 } - Requires System.Linq


Dictionary

To use Dictionary, you need to import System.Collections.Generic.

  using System.Collections.Generic;

  Dictionary<string, int> dictionary = new Dictionary<string, int>();
  Dictionary<string, int> dictionary = new Dictionary<string, int>()
  {
      { "1", 1 },
      { "2", 2 }
  };

  // Key and Value may be System.ValueTuple
  Dictionary<(string, string), (int, int)> dictionary = new Dictionary<(string, string), (int, int)>();


Operations on Dictionnary

  using System.Collections.Generic;
  using System.Linq;

  Dictionary<string, int> dictionary = new Dictionary<string, int>();


  /**** Basic Operations ****/
  int size = dictionary.Count(); // Get number of elements of dictionary

  string key = "1"; int val = 1;
  dictionary.Add(key, val); // Add key and value - if key already in dictionary, throw error

  var extractedValue = dictionary[key] // Exctract Value associated with key

  bool b = dictionary.ContainsKey(key); // Check if key already in dictionary


  /**** Extract Keys and Values ****/
  List<string> keys = dictionary.Keys.ToList(); // Get Keys in List - Can also cast to array - Requires System.Linq
  int[] values = dictionary.Values.ToArray(); // Get Values in array - Can also cast to List - Requires System.Linq


  /**** Comparison Operations ****/
  string minKey = dictionary.Keys.Min(); // Get minimum Key
  var maxKey = dictionary.Keys.Max(); // Get maximum Key

  var minValue = dictionary.Values.Min(); // Get minimum Value
  int maxValue = dictionary.Values.Max(); // Get maximum Value

  string minKeyByValue = dictionary.MinBy(kvp => kvp.Value).Key; // Get Key with min Value - Requires System.Linq - Work with .Net 6
  string maxKeyByValue = dictionary.MaxBy(kvp => kvp.Value).Key; // Get Key with max Value - Requires System.Linq - Work with .Net 6


  /**** Loop ****/
  foreach(KeyValuePair<string, int> entry in dictionary) {
      // do something with entry.Value or entry.Key
  }


Tuples

System.ValueTuple vs System.Tuple

C# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types.

  (string, int) t1 = ("test", 3); // System.ValueTuple
  Tuple<string, int> t2 = new Tuple<string, int> ("test", 3); // System.Tuple


Here are their main differences:

System.ValueTuple System.Tuple
value types reference types
mutable immutable
Data members are fields. Data members are properties.


This StackOverflow post says that in general, using System.ValueTuple is preferable to reference-type System.Tuple, because:


System.ValueTuple

Basic initialization
  (string, int) t1 = ("test", 3);
  (double, int) t = (4.5, 3);
  var t =  (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);


Using field names
  var t1 = (tupleSum: 4.5, tupleCount: 3);
  (double tupleSum, int tupleCount) t2 = (4.5D, 3);
  (double tupleSum, int tupleCount) t3 = (Sum: 4.5D, Count: 3);

  double tupleSum = 4.5; int tupleCount = 3;
  var t4 = (tupleSum, tupleCount);

  (string, int) t5 = (tupleSum: 1.2, tupleCount: 6); // Sum and Count will be ignored. It will have the default Item1, Item2


Operations on System.ValueTuple

  (string, int, int) t1 = ("test", 3, 7);
  (double tupleSum, int tupleCount) t2 = (4.5, 3);


  /**** Access ****/
  var s = t1.Item1; // "test"
  int i1 = t1.Item2; // 3
  dynamic i2 = t1.Item3; // 7

  double d = t2.tupleSum; // 4.5
  var i3 = t2.tupleCount; // 3


  /**** ToTuple ****/
  var t3 = t1.ToTuple();
  Tuple<double, int> t4 = t2.ToTuple();


  /**** Function returning Tuple ****/
  public (int, int, string) FunctionReturnTuple() { return (0, 1, "example"); }
  var tuple = FunctionReturnTuple(); // tuple.Item1 = 0 ; tuple.Item2 = 1 ; tuple.Item = "example" ;

  // Deconstruct with field names
  (int firstInt, int secondInt, string firstString) = FunctionReturnTuple();
  var (firstInt, secondInt, firstString) = FunctionReturnTuple();

  // Declare with fields names
  public (int firstInt, int secondInt, string firstString) FunctionReturnTuple() { return (0, 1, "example"); }
  var tuple = FunctionReturnTuple(); // tuple.firstInt = 0 ; tuple.secondInt = 1 ; tuple.firstString = "example" ;

  public (int, int, string) FunctionReturnTuple() { return (firstInt: 0, secondInt: 1, firstString: "example"); }
  var tuple = FunctionReturnTuple(); // tuple.firstInt = 0 ; tuple.secondInt = 1 ; tuple.firstString = "example" ;

  // Declared fields names can be overrided by other field names at deconstruction
  public (int firstInt, int secondInt, string firstString) FunctionReturnTuple() { return (0, 1, "example"); }
  var (first, second, third) = FunctionReturnTuple(); // OK
  var (secondInt, second, third) = FunctionReturnTuple(); // OK - secondInt = 0 - if same field names are used but in another order, order prevail


System.Tuple

  var t1 = new Tuple<string, int> ("test", 3);
  Tuple<string, int> t2 = new Tuple<string, int> ("test", 3);

  var t3 = Tuple.Create("test", 3, "7", false);


System.Tuple can’t have custom field names.


Operations on System.Tuple

  Tuple<string, int> t1 = new Tuple<string, int> ("test", 3);


  /**** Access ****/
  var s = t1.Item1; // "test"
  int i1 = t1.Item2; // 3


  /**** ToValueTuple ****/
  var t2 = t1.ToValueTuple();
  (string, int) t3 = t1.ToValueTuple();
  (string Name, int tupleCount) t4 = t1.ToValueTuple(); // Can transform in ValueTuple with name fields


Queue

To use Queue, you need to import System.Collections.Generic.
Queue is a FIFO (or LILO) data structure.

  using System.Collection.Generic;

  Queue<string> queue = new Queue<string>();
  Queue<int> queue = new Queue<int>();
  Queue<(int, string)> queue = new Queue<(int, string)>();

  Queue<int> queue = new Queue<int>(new[] { 1, 2, 3 }); // Initialization with value


Operations on Queue

  using System.Collection.Generic;
  Queue<int> queue = new Queue<int>();


  /**** Size ****/
  int length = queue.Count; // 0


  /**** Insertion: Enqueue ****/
  queue.Enqueue(1); // Add to the Queue
  queue.Enqueue(2); // Add to the Queue


  /**** Comparison Operations ****/
  int min = queue.Min(); // 1 - Minimum of Queue
  int max = queue.Max(); // 2 - Maximum of Queue


  /**** Aggregation Operations ****/
  int sum = queue.Sum(); // 3 - Sum of Queue
  double average = queue.Average(); // 1.5 - Average of Queue


  /**** Access: Dequeue ****/
  int i1 = queue.Dequeue(); // 1 - Dequeue oldest element
  int i2 = queue.Dequeue(); // 2 - Dequeue oldest element - Dequeue empty Queue throw error


  /**** While Loop ****/
  while(queue.Count > 0){
    // Do something
  }


Stack

To use Stack, you need to import System.Collections.Generic.
Stack is a FILO (or LIFO) data structure.

  using System.Collection.Generic;

  Stack<string> stack = new Stack<string>();
  Stack<int> stack = new Stack<int>();
  Stack<(int, string)> stack = new Stack<(int, string)>();


Operations on Stack

  using System.Collection.Generic;
  Stack<int> stack = new Stack<int>();


  /**** Size ****/
  int length = stack.Count; // 0


  /**** Insertion: Push ****/
  stack.Push(1); // Add to the Stack
  stack.Push(2); // Add to the Stack


  /**** Comparison Operations ****/
  int min = stack.Min(); // 1 - Minimum of Stack
  int max = stack.Max(); // 2 - Maximum of Stack


  /**** Aggregation Operations ****/
  int sum = stack.Sum(); // 3 - Sum of Stack
  double average = stack.Average(); // 1.5 - Average of Stack


  /**** Access: Pop ****/
  int i1 = stack.Pop(); // 1 - Pop newest element
  int i2 = stack.Pop(); // 2 - Pop newest element - Pop empty Stack throw error


  /**** While Loop ****/
  while(stack.Count > 0){
    // Do something
  }


Ressources

See: