Skip to content

iqfareez/MCTE-4327-Software-Engineering

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C# .Net

MCTE 4327 Software Engineering

Table of Contents

Chapter 1 - Introduction

im a god programmer

Software is just a collection of instruction, to make a rock doing something useful.

In other words, software is a collections of computer instruction that tell the hardware how to perform the task.

Meanwhile, hardware is the physical matter that run the software.

Programming languages

Modern programming languages, like C#, Python, Dart etc, are built on top of lower level programming languages.

Machine language

  • Machine language is a low-level programming language that uses the binary numerals or hex to program computers.
  • Native language of a particular processor architecture. Talks directly to the bare metal. Human barely can understand them.
  • Processor dependent: a program written in one processor's machine language will not work on another processor without translation.
  • Machine language is sometimes called machine code or binary code.

Assembly language

  • Low-level programming language for a computer, or other programmable device.
  • Each assembly language is specific to a particular computer architecture.
  • Some syntax are understandable by human, but hardly to program it.
  • It uses assembler to translate the assembly language into machine language.

C# language

  • C# is a general-purpose, multi-paradigm programming language encompassing strong typing, imperative, declarative, functional, generic, object-oriented, and component-oriented programming disciplines.
  • Developed by Microsoft within its .NET initiative and later approved as a standard by Ecma (ECMA-334) and ISO (ISO/IEC 23270:2018).

Operating System

os

  • An operating system (OS) is system software that manages computer hardware and software resources and provides common services for computer programs.
  • Provide API for the application to interact with the hardware.

Takeaways

  • Modern programming languages built on multiple layers of abstraction.
  • The lower the level of abstraction, the easier to talk to the bare metal.
  • The higher the level of abstraction, the easier to understand by human and job can be done faster.

⬆ Back to top

Chapter 2 - Software Engineering Principles

code fvck

Definition

mindmap
  root((Software))
    Sofware need to be engineered
    Software doesn't wear out, but detoriate over time
    Software is complex
Loading

Sofware rot

~ is low detoriation of software performance over time or its deminishing responsiveness. Example of software rot is when a software is not maintained and updated, or change in environment (eg: Windows XP to Windows 10, though, I've heard that Microsoft will keep as much backward compatibility as possible).

Some companies may have plan to deprecate the software after some times. For example, Microsoft have declared End of Life (EOL) for Windows 7 and Windows 8.1 in favor of the newer versions of Windows. Here is another example of Adobe software that also have been declared end of support.

Software release lifecycle

graph LR
subgraph Testing
PA[Pre-Alpha] --> A[Alpha]
A --> B[Beta]
B --> RC[RC]
end
subgraph Release
RC --> RTM[RTM]
RTM --> D[GA/Stable]
end
Loading
Phase Description
Pre-Alpha The earliest stage of development where the software is not yet feature-complete and may be unstable or unusable. Tested internally
Alpha The software has basic functionality but may still have significant bugs or issues. Also tested internally
Beta The software is feature-complete and released to a limited audience for further testing and feedback. Start to test publicly
Release Candidate (RC) A version of the software that is intended to be very close to the final version, bug fixing may be needed before the official release.
Release To Manufacturing (RTM) A term used primarily in software development for the commercial release of a product to the market. (It's my first time knowing this term)
General Availability (GA) or Stable The official release of the software that is considered stable and ready for general use.

Info The product MVP (Minimum Viable Product) is typically developed and released during the Alpha or Beta stages of the software release lifecycle.

You can see the example of the release process in many OSS, for example, in the arduino-ide development. Notice that, they started to release beta version publicly in the 2.0.0-beta.1 on 12 Feb 2021. After beta.12, they move to RC stages begin with 2.0.0-rc1. Finally, the release the stable version 2.0.0 on 14 September 2022. Oh btw, software versioning is another interested topic to learn.

Software Development Lifecycle (SDLC)

aka Software Development Methodology

A process for planning, creating, testing, and deploying an application or system.

In general

graph LR
subgraph Planning
P[Planning] --> R[Requirements]
R --> D[Design]
end
subgraph Development
D --> C[Code]
C --> T[Test]
end
subgraph Deployment
T --> I[Install]
I --> M[Maintenance]
end
Loading

Some examples of popular SDLC

Methodology Definition Pros Cons
Waterfall A linear, sequential approach to software development in which each phase (requirements, design, implementation, testing, deployment, and maintenance) must be completed before moving on to the next. Easy to understand and manage, provides a clear path from start to finish, works well for projects with well-defined requirements. Can be slow and inflexible, makes it difficult to respond to changing requirements or feedback, can result in a product that doesn't meet user needs.
Incremental A method of software development in which the project is divided into smaller parts or "increments," with each increment building on the previous one. Each increment includes the entire software development life cycle. Allows for early and frequent delivery of working software, makes it easier to respond to changes or feedback, reduces the risk of project failure by identifying problems early. Can be difficult to plan and manage, may require more resources and time than other methods, may result in a less cohesive product if not properly integrated.
Prototype A method of software development in which a preliminary version of the software is created to test and evaluate ideas, concepts, and designs. Allows for early and frequent feedback, enables rapid iteration and improvement, can help clarify and refine requirements. Can be difficult to manage and control, may result in a product that doesn't meet user needs if not properly tested, can be costly and time-consuming.
Spiral A method of software development in which the project is divided into smaller parts, each of which goes through a series of iterative steps, including planning, risk assessment, engineering, and evaluation. Enables frequent and thorough risk assessment, allows for early and frequent feedback, enables continuous improvement and refinement. Can be complex and difficult to manage, may require more resources and time than other methods, may result in a less cohesive product if not properly integrated.
Agile A method of software development that emphasizes collaboration, flexibility, and rapid iteration. Work is divided into small, self-contained units called "sprints," with each sprint delivering a working increment of the software. Enables rapid response to changing requirements or feedback, encourages collaboration and communication among team members, allows for early and frequent delivery of working software. Can be difficult to plan and manage, may require more resources and time than other methods, may result in a less cohesive product if not properly integrated.

Requirement engineering

The process of identifying, documenting, and managing requirements for a product, system, or software to satisfy customer needs and business objectives.

Stages

  1. Inception - Asks basic questions about the project, such as what the project is, why it is needed, and who will use it.
  2. Elicitation - Address problems of scope/understanding/volatility.
  3. Elaboration - Analysis model that identifies data, function, features, constraints and behavioral requirements.
  4. Negotiation - Agree on a deliverable system
  5. Specification

UML Diagrams

UML (Unified Modeling Language) diagrams that are commonly used in software engineering to help design and document software systems.

Some of the examples including:

Use case diagram

Class diagram

Sequence diagram

Activity diagram

⬆ Back to top

Chapter 3 - Primitive Data Types

Built in types

Below are some examples:

  • int - 32-bit signed integer
  • double - 64-bit floating point number
  • char - 16-bit Unicode character
  • bool - Boolean value

Read more on docs

Reference VS Value Types

Value types are types that hold their values directly and are stored on the stack. Usually doesn't require a new keyword to declare the value. [Docs]

Reference types, hold a reference to a memory location where the actual data is stored on the heap. Usually requires a new keyword to create an instance of the type. [Docs]

reference type

Example of value types: int, bool, char, float, enum, struct.

Example of reference types: string, object, array, delegate.

Example: Assume Coordinate is a Class:

var pos1 = new Coordinate(101.2, 3.14);
var pos2 = pos1;

pos1.latitude = 300.2;

Console.WriteLine(pos1); // 300.2,3.14
Console.WriteLine(pos2); // 300.2,3.14

Since we have pos2 = pos1, pos2 is a reference to pos1. So, when we change the value of pos1, pos2 will also change.

Now, let's change the Coordinate object from Class to a struct. Now, both value are independent. Changes with pos1 will not affect pos2.

...
Console.WriteLine(pos1); // 300.2,3.14
Console.WriteLine(pos2); // 101.2,3.14

There are some other examples, visit here and here.

var keyword

The var keyword is used to declare a variable of a type that is inferred from the value of the expression on the right side of the assignment operator. [Docs]

var a = 10; // a is an int
var b = 10.5; // b is a double
var c = "Hello World UwU"; // c is a string

Arrays

An array is a data structure that contains a group of elements of the same type. [Docs]

Example:

int[] foo = new int[5]; // declare an array of 5 integers
foo[0] = 1;
foo[1] = 2;
foo[2] = 3;
foo[3] = 4;
foo[4] = 5;

or you can do it in one line:

int[] foo = new int[5] {1, 2, 3, 4, 5};

Iteration Statements

for-loop

The for loop is used to iterate a part of the program several times. [Docs]

Take the array example above, we can use for loop to iterate through the array:

for (int i = 0; i < foo.Length; i++)
{
    Console.WriteLine(foo[i]);
}

foreach-loop

The foreach loop is used to iterate through the elements of a collection. [Docs]

foreach (int i in foo)
{
    Console.WriteLine(i);
}

Takeways

for loop allows us to access the array element and modify it (if we wanted to). Meanwhile, foreach loop only allows us to read the array element but not modify it.

Default value

The default value of a variable is the value that is assigned to it when it is declared. The default value of a variable depends on the type of the variable. [Docs]

int a; // a is 0
double b; // b is 0
string c; // c is null
bool d; // d is false

Note Any reference type will default to null if not initialized.

Note that the default value only be asssigned for member variables of a class/struct. Local variables must be initialized before use. See example code here.

For example, consider the code below:

static void Main(string[] args)
{
    int a;
    Console.WriteLine(a);
}

The code above will yield an error: Use of unassigned local variable 'a'.

Let's consider another example:

class Meow
{
    public int a;
}

class Program
{
    static void Main(string[] args)
    {
        var meow = new Meow();
        Console.WriteLine(meow.a); // 0
    }
}

This time, the uninitialized variable a will be assigned the default value of 0. No error will be thrown.

Anonymous Types

Anonymous types are used to create an object without having to explicitly define a type. [Docs]

var kuceng = new { Name = "comot", Age = 4 };
Console.WriteLine(kuceng.toString()); // { Name = comot, Age = 4 }

However, anonymous types are immutable. You cannot change the value of the properties of an anonymous type.

kuceng.Name = "mikail"; // error

Methods

Methods? Oh, when a function is a part of a class, it's called a method.

C# is an OOP language and doesn't have functions that are declared outside of classes, that's why all functions in C# are actually methods. Though, beside this formal difference, they are the same...

A method is a block of code that performs a specific task. [Docs]

Basic method

private static void PrintHello()
{
    Console.WriteLine("Hello world");
}

Method with return value

private static int AddNumber(int a, int b)
{
    return a + b;
}

Method with multiple return values (Tuple)

static (string, int) LookupCat()
{
    return ("Cicik", 12);
}

// you can 'unpack' the tuple value:
(string catName, int catAge) = LookupCat();
Console.WriteLine(catName); // Cicik
Console.WriteLine(catAge); // 12

String

A string is an object of type String whose value is text. [Docs]

var boo = "my message here";
Console.WriteLine(boo.Length); // 15

// substring
Console.WriteLine(boo.Substring(4, 8)); // essage h

// Splitting string. By default, the delimiter is " "
var s = boo.Split(); // ["my", "message", "here"]

String vs string? string (lowercase) is just an alias for String (uppercase) from the System namespace, which means you can use either keyword to declare a string variable or to use any of the string class's methods.

toString() method

The toString() method is used to represent the value of an object as a string. [Docs]

int z = 80;
DisplayMessage(z.ToString()); // 80

You can override the toString() method in your class.

Say you have the class below:

 class City
{
    public string Name;
    public int Population;
}

Then,

City petalingJaya = new City();
petalingJaya.Name = "Petaling Jaya";
petalingJaya.Population = 1000000;

Console.WriteLine(petalingJaya); // ConsoleApplication2.City

The output will be your namespace+class name. The output is perhaps not very useful. You can override the toString() method to make it more useful.

public override string ToString()
{
    return $"{Name}: {Population}";
}
...
Console.WriteLine(petalingJaya); // Petaling Jaya: 1000000

DateTime

Represents an instant in time, typically expressed as a date and time of day. [Docs]

// Get current date & time
DateTime now = DateTime.Now;
Console.WriteLine(now); // 2021-10-10 11:00:00

// format dateTime
Console.WriteLine(now.ToString("HH:mm, dddd")); // 11:00, Sunday

Parsing

Usually from literal string to a specific type. Eg: string ➡️ int

int.Parse("123"); // 123
int arg1 = int.Parse(args[0]);

Exception Handling

try/catch meme

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions. [Docs]

By catching exceptions, you can handle errors in a controlled and graceful manner.

try
{
    // code that might throw an exception
    int.Parse("abc");
}
catch (Exception ex)
{
    // code to handle the exception
    Console.WriteLine(ex.Message); // "Input string was "not in a correct format.
}

Random Number

Represents a pseudo-random number generator, which is an algorithm that produces a sequence of numbers that meet certain statistical requirements for randomness. [Docs]

Random random = new Random();
int diceNumber = random.Next(1, 7);
Console.WriteLine(diceNumber); // 1-6

⬆ Back to top

Chapter 4 - Object Oriented Programming

meme oop cat table

4 Pillars of OOP

Object Oriented Programming (OOP) is a popular programming paradigm that focuses on creating objects which contain both data and behavior. It is based on the concepts of encapsulation, inheritance, and polymorphism, and provides a way to organize and structure code in a more intuitive and efficient manner.

4 OOP Pillars

Encapsulation

Encapsulation refers to the idea of bundling data and methods within a single unit, and restricting access to the data from outside that unit. In C#, encapsulation is achieved through the use of access modifiers such as public and private, and properties with getter and setter methods.

public and private access modifiers

To control wether members/methods can be accessed from outside the class.

  • public - member/method can be accessed from anywhere.
  • private - member/method can only be accessed from within the class.
class BankAccount
{
    private double balance;

    public double GetBalance()
    {
        return balance;
    }

    public void Deposit(double amount)
    {
        balance += amount;
    }

    public void Withdraw(double amount)
    {
        balance -= amount;
    }
}

getter and setter methods

Getter and setter methods are used to access and modify the value of a private field. They are also known as accessor and mutator methods.

Example 1: allow read but prevent write from outside of class

class BankAccount
{
    private double balance;

    public double Balance
    {
        get { return balance; }
        private set { balance = value; }
    }

    public void Deposit(double amount)
    {
        Balance += amount;
    }

    public void Withdraw(double amount)
    {
        Balance -= amount;
    }
}

Example 2: Make methods behave like properties

class Person
{
    private string name;
    private int age;

    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public string Introduction
    {
        get { return "Hi, my name is " + name + " and I am " + age + " years old."; }
    }
}

// in Main()
Person person = new Person("John", 30);
Console.WriteLine(person.Introduction);
// Output: Hi, my name is John and I am 30 years old.

static keyword

A static member is associated with the type itself rather than with a specific object. You access static members without instantiating the class. [Docs]

public static class TemperatureConverter
{
    public static double CelsiusToFahrenheit(string temperatureCelsius)
    {
        // Convert argument to double for calculations.
        double celsius = Double.Parse(temperatureCelsius);

        // Convert Celsius to Fahrenheit.
        double fahrenheit = (celsius * 9 / 5) + 32;

        return fahrenheit;
    }
}

class TestTemperatureConverter
{
    static void Main()
    {
        F = TemperatureConverter.CelsiusToFahrenheit(98.2);
    }
}

Operator Overloading

Define the behavior of an operator in a way that is specific to the type of data you are using. [Docs]

Operators that can be overloaded including: +, -, *, /, &, ==, and so on. See full list here.

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public static Point operator +(Point p1, Point p2)
    {
        return new Point(p1.X + p2.X, p1.Y + p2.Y);
    }
}

static void Main()
{
    Point p1 = new Point(1, 2);
    Point p2 = new Point(3, 4);
    Point p3 = p1 + p2;
    Console.WriteLine(p3.X); // 4
    Console.WriteLine(p3.Y); // 6
}

Inheritance

~ is a fundamental concept in OOP that allows you to create new classes based on existing ones. Inheritance enables you to reuse code from a parent class (also known as a superclass or base class) and extend or modify it in a child class (aka a subclass or derived class). This makes your code more organized, easier to read, and less repetitive. [Docs]

using System;

class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public virtual void Speak()
    {
        Console.WriteLine($"{Name} makes a noise.");
    }
}

class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    public override void Speak()
    {
        Console.WriteLine($"{Name} barks.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dog dog = new Dog("Rex");
        dog.Speak(); // Rex barks.
    }
}

base keyword

The base keyword is used to access members of the base class from within a derived class. [Docs]

class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public virtual void Speak()
    {
        Console.WriteLine($"{Name} makes a noise.");
    }
}

class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    public override void Speak()
    {
        base.Speak();
        Console.WriteLine($"{Name} barks.");
    }
}

Preventing inheritance

To prevent a class from being inherited, use the sealed keyword. [Docs]

sealed class Animal
{
    // implementation
}

Polymorphism

Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object. [Docs]

Upcasting & Downcasting

Upcasting is the process of converting a reference of a derived class to a base class. This is done automatically when you assign a derived class object to a base class reference. [Docs]

// Assume Car class is inherited from Vehicle class
Car A = new Car();
A.brand = "Myvi";
A.type = "Compact";

// Upcasting (from Car to Vehicle)
Vehicle B = A;

Console.WriteLine(B.type); // Compact

// Downcasting (from Vehicle to Car)
Car C = (Car)B;
Console.WriteLine(C.brand); // Myvi
Console.WriteLine(C.type); // Compact

Abstract class

An abstract class is a class that is declared with the abstract keyword. An abstract class may not be instantiated, but it may be inherited by non-abstract subclasses. [Docs]

abstract class Painter
{
   // Fields
   protected string _name;
   protected int _age;

   // Constructors
   public Painter(string name, int age)
   {
      _name = name;
      _age = age;
   }

   // Abstract method
   public abstract void Paint();

   // Non-abstract method
   public void Introduction()
   {
      Console.WriteLine($"My name is {_name} and I am {_age} years old.");
   }
}

class FamousPainter : Painter
{
   public FamousPainter(string name, int age) : base(name, age)
   {
   }

   // Implementation of abstract method
   public override void Paint()
   {
      Console.WriteLine("I am painting a masterpiece!");
   }
}

class Program
{
   static void Main(string[] args)
   {
      FamousPainter painter = new FamousPainter("Leonardo", 67);
      painter.Introduction();
      painter.Paint();

      Console.ReadKey();
   }
}

Interface

An interface is a reference type that defines a set of methods, properties, events, or indexers that a class or struct must implement. [Docs]

interface IShape {
    void Draw();
}

interface IMovable {
    void Move();
}

class Circle : IShape, IMovable {
    public void Draw() {
        Console.WriteLine("Drawing a circle.");
    }

    public void Move() {
        Console.WriteLine("Moving a circle.");
    }
}

class Program {
    static void Main(string[] args) {
        Circle circle = new Circle();
        circle.Draw();
        circle.Move();
    }
}

Abstract class vs Interface

Abstract Class Interface
Implementation Can have both abstract and non-abstract methods. Can only have abstract methods.
Inheritance A class can inherit from only one abstract class. A class can implement multiple interfaces.
Fields Can have fields. Cannot have fields.
Constructors Can have constructors. Cannot have constructors.
Usage Typically used to define a base class for a family of related classes. Used to define a contract that a class must follow.
Flexibility Provides more implementation flexibility. Provides more flexibility in defining the behavior of a class.

object

The object type is the root of the type hierarchy. Every type in C# directly or indirectly derives from the object type. [Docs]

object obj = 123;
Console.WriteLine(obj); // 123

Every object has the following methods:

  • Equals() - Determines whether the specified object is equal to the current object.
  • GetHashCode() - Serves as the default hash function.
  • GetType() - Gets the Type of the current instance.
  • ToString() - Returns a string that represents the current object.
Boxing & unboxing

Boxing is the process of converting a value type to the object type or to any interface type implemented by this value type. Unboxing is the reverse process. [Docs]

string food = "cekodok";

// boxing - converting a value type to the object type
object foodObj = food;

// unboxing - converting an object type to a value type
string foodStr = (string) foodObj;

Console.WriteLine(foodStr); // cekodok

Generic method

A generic method is a method that can be called with arguments of different types. [Docs]

static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

int a = 1, b = 2;
Swap(ref a, ref b);
Console.WriteLine(a); // 2
Console.WriteLine(b); // 1

The example above passes type int to the method. But, it is also possible to pass other types such as string, float, etc.

Generic class

A generic class is a class that can be called with arguments of different types. [Docs]

class GenericList<T>
{
    public void Add(T input) { }
}

var list = new GenericList<int>();
list.Add(1);

Why use Generic class/method?

  • Reusability: When you want to create a class that can work with different data types.
  • Collections: Commonly used with collections such as List<T> or Dictionary<TKey, TValue>. This allows you to create collections that are strongly typed, meaning that you can only add or retrieve objects of a specific type.

Chapter 5 - Event Driven SE

Event driven cat meme

Delegate

A pointer to a function. [Docs]

public delegate int Transformer(int x);

Transformer square = x => x * x;
var ans = square(3); // or square.Invoke(3)
Console.WriteLine(ans); // 9

Note - The => operator is called the lambda operator. It is used to define an anonymous method. [Docs]

An interesting and useful property of delegate is that it does not know or care about the class of the methods it references; all that matters is that the referenced method has the same parameters and return type as the delegate. - The C# Programming Language

Built-in delegates

A built-in delegate is a pre-defined delegate type that is provided by the .NET Framework.

flowchart TB
    A[Built-in Delegates] --> B[Action]
    A --> C[Func]
    A --> D[Predicate]
Loading

Note - The built-in delegate can have up to 16 input parameters.

Action

Represents a method that has a void return type and takes zero or more input parameters.. [Docs]

Action<string> print = Console.WriteLine;
print("Hello World"); // Hello World

Func

Represents a method that has a return type and takes zero or more input parameters. [Docs]

Func<int, int, int> add = (x, y) => x + y;
var ans = add(3, 4); // or add.Invoke(3, 4)
Console.WriteLine(ans); // 7

Note - The last parameter of the Func delegate is always the return type.

Event & EventHandler

An event is a notification sent by an object to signal the occurrence of an action. [Docs]

c# events diagram

When a method is declated using the event keyword, you are creating a multicast delegate that other objects can subscribe to in order to receive notifications when the event is raised.

What is EventHandler? It is a built-in delegate that represents a method that handles an event. [Docs]

Example:

public class Counter
{
    // Define an event called CountReached that can be subscribed to by other parts of the code
    public event EventHandler CountReached;

    // Define a method that counts from 1 to the specified number and raises the CountReached event
    public void CountTo(int countTo)
    {
        for (int i = 1; i <= countTo; i++)
        {
            Console.WriteLine("Counting: " + i);

            // Check if the current count value equals the specified countTo value
            if (i == countTo)
            {
                // Raise the CountReached event is not null
                OnCountReached();
            }
        }
    }

    private void OnCountReached()
    {
        // Invoke the CountReached event
        CountReached?.Invoke(this, null);
    }
}

Then, in the Main() method:

// Create an instance of the Counter class
Counter counter = new Counter();

// Subscribe to the CountReached event of the Counter instance
counter.CountReached += (a, e) => Console.WriteLine("Count reached");

// Start counting from 1 to 5
counter.CountTo(5);

In the above example, we create a simple Counter class that counts from 1 to a specified number and raises an event called CountReached whenever it reaches the specified number.

Output:

Counting: 1
Counting: 2
Counting: 3
Counting: 4
Counting: 5
Count reached

Subscribe/Unsubscribe to an event

Subscribing to an event by using the += operator with an event handler delegate. You can unsubscribe from an event by using the -= operator with the same event handler delegate.

// Subscribe to the event
counter.CountReached += (a, e) => SomeMethod();

// Unsubscribe to the event
counter.CountReached -= (a, e) => SomeMethod();

Null safety

Notice that the above example, we use the ?. operator to invoke the CountReached event. This is because the CountReached event may be null if no one has subscribed to it.

The ?. operator is called the null-conditional operator. It is used to access a member of a variable that may be null. [Docs]

// The following code will throw an exception if the CountReached event is null
counter.CountReached.Invoke(this, null);

⬆ Back to top

Chapter 6 - Data Structures

Data structures meme

Array

An array is a data structure that contains a group of elements. [Docs]

  • Implicit base class for all single and multidiemensional arrays
  • An aray can contain value-type or reference-type elements.
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;

Equality check

Even though two arrays can contains the same elements, it will fail the equality check because they are two different objects.

string[] arr1 = { "a", "b", "c" };
string[] arr2 = { "a", "b", "c" };
Console.WriteLine(arr1 == arr2); // False

Finding items

Finding one item

Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire Array. [Docs]

using System;

string[] cats = { "Tom", "Jerry", "Coklat", "Garfield", "Comel" };
var match = Array.Find(cats, s => s.StartsWith("C"));
// Result: match = "Coklat"
Finding multiple items

Retrieves all the elements that match the conditions defined by the specified predicate. [Docs]

using System;

string[] cats = { "Tom", "Jerry", "Coklat", "Garfield", "Comel" };
var matches = Array.FindAll(cats, s => s.StartsWith("C"));
// Result: matches = {"Coklat", "Comel"}

Sort

Sorts the elements in a one-dimensional array. This will sort the elements of the array in ascending order. [Docs]

using System;

string[] cats = { "Tom", "Jerry", "Coklat", "Garfield", "Comel" };
Array.Sort(cats);
// Output: { "Coklat", "Comel", "Garfield", "Jerry", "Tom" }

Reverse sort

To sort the elements in descending order, you can use the Array.Reverse() method after using Array.Sort(). It reverses the order of the elements in a one-dimensional Array or in a portion of the Array. [Docs]

string[] cats = { "Tom", "Jerry", "Coklat", "Garfield", "Comel" };
Array.Sort(cats);
Array.Reverse(cats);
// Output: { "Tom", "Jerry", "Garfield", "Comel", "Coklat" }

List

Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists. [Docs]

using System.Collections.Generic;

// Initializing List
List<string> hariRayaDishes = new List<string>() { "Rendang", "Satay", "Ketupat", };

// Add item to list
hariRayaDishes.Add("Lontong");
hariRayaDishes.Add("Dodol");

// Remove item from list
hariRayaDishes.Remove("Satay");
// alternatively, remove by index:
// hariRayaDishes.RemoveAt(1);

// number of items in list
Console.WriteLine(hariRayaDishes.Count);

// check if item exists in list
hariRayaDishes.Contains("Rendang"); // true
hariRayaDishes.Contains("Mee Kari"); // false

LinkedList

A collection of nodes where each node contains a value and a reference to the next node in the list. It is a data structure that allows for efficient insertion and removal of items from the list without requiring the elements to be stored in contiguous memory locations like an array. [Docs]

using System.Collections.Generic;

// Initializing LinkedList
LinkedList<string> hariRayaDishes = new LinkedList<string>();
hariRayaDishes.AddLast("Rendang");
hariRayaDishes.AddLast("Satay");
hariRayaDishes.AddLast("Ketupat");

// Add item to list
hariRayaDishes.AddLast("Dodol");
hariRayaDishes.AddAfter(hariRayaDishes.First.Next.Next, "Lemang");
// alternatively, find specific node and add after it:
// hariRayaDishes.AddAfter(hariRayaDishes.Find("Satay"), "Lemang");

// Remove item from list
hariRayaDishes.Remove("Satay");

// number of items in list
Console.WriteLine(hariRayaDishes.Count);

// check if item exists in list
hariRayaDishes.Contains("Rendang"); // true
hariRayaDishes.Contains("Mee Kari"); // false

Queue<T>

A collection of objects that supports fast first in, first out (FIFO) semantics for inserts (Enqueue) and removes (Dequeue). [Docs]

Queue<string> ticketQueue = new Queue<string>();

// Adding tickets to the queue
ticketQueue.Enqueue("Ticket #1");
ticketQueue.Enqueue("Ticket #2");
ticketQueue.Enqueue("Ticket #3");

// Check who come next without dequeuing it
var tempTicket = ticketQueue.Peek();

// Serving tickets in the queue
while (ticketQueue.Count > 0)
{
    // ticket will assigned to the topmost element
    string ticket = ticketQueue.Dequeue();
    Console.WriteLine($"Serving ticket: {ticket}");
}

Output:

Serving ticket: Ticket #1
Serving ticket: Ticket #2
Serving ticket: Ticket #3

Stack<T>

A collection of objects that supports fast last in, first out (LIFO) semantics for inserts (Push) and removes (Pop). [Docs]

Stack<string> browserStack = new Stack<string>();
browserStack.Push("https://www.google.com");
browserStack.Push("https://www.facebook.com");

// Simulating a user browsing history
Console.WriteLine("User is currently on: " + browserStack.Peek());
browserStack.Push("https://www.linkedin.com");
Console.WriteLine("User visited: " + browserStack.Peek());

// Implementing the back button functionality
Console.WriteLine("User clicked back button...");
browserStack.Pop();
Console.WriteLine("User is currently on: " + browserStack.Peek());

Output:

User is currently on: https://www.facebook.com
User visited: https://www.linkedin.com
User clicked back button...
User is currently on: https://www.facebook.com

Dictionary<TKey, TValue>

A generic class that represents a collection of key-value pairs, where each key is unique within the dictionary. [Docs]

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

// Add some names and ID to the dictionary
names.Add(143, "Alice");
names.Add(211, "Bob");
names.Add(342, "Charlie");

// Retrieve a name using its ID
string name = names[342];

// Remove a name from the dictionary
names.Remove(211);

// Check if a key exists in the dictionary
if (names.ContainsKey(143))
{
    Console.WriteLine("ID 143 belongs to: " + names[143]);
}

// Iterate over the remaining names in the dictionary
foreach (KeyValuePair<int, string> kvp in names)
{
    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
}

Output:

ID 143 belongs to: Alice
143: Alice
342: Charlie

Example usage in daily life

data structures used in daily life

[Source]

⬆ Back to top

Chapter 7 - Graphical User Interface (GUI)

~ is a type of interface that allows users to interact with digital devices using visual elements such as icons, menus, and buttons, rather than text-based commands.

Windows Forms

Windows Forms is a graphical user interface (GUI) subsystem provided by the .NET Framework. It is a managed code framework that provides a set of classes and other resources that you can use to create Windows-based applications. [Docs]

label

Example: Simple Form with Button that opens up MessageBox.

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    var form1 = new Form();
    form1.Text = "Form 1";
    form1.Width = 400;
    form1.Height = 300;

    Label label1 = new Label();
    label1.Text = "Welcome to this application";
    label1.Font = new System.Drawing.Font("Arial", 15);
    label1.Width = 300;
    label1.Height = 70;
    label1.Left = 40;
    label1.Top = 20;

    Button button1 = new Button();
    button1.Text = "Click me!";
    button1.Width = 100;
    button1.Height = 50;
    button1.Left = 150;
    button1.Top = 100;
    button1.Click += (a ,e) => MessageBox.Show("I've been clicked!", "My Dialog");

    form1.Controls.Add(label1);
    form1.Controls.Add(button1);

    Application.Run(form1);
}

example1

Some examples:

Update text for a label

answerLabel.Text = "Answer: " + output.ToString();

Opening a new Window/Form

var myForm = new Form2();
myForm.Show();

Adding row to DataGridView

for (var i = 0; i < lines.Length; i += 1)
{
    var data = lines[i].Split(',');
    dataGridView1.Rows.Add(data);
}

Timer

A timer is a control that raises an event at specified intervals. [Docs]

In the form, add a Timer component from System.Windows.Forms namespace and a Label component. The interval value of the Timer is set to 20 (ms).

// Tick event handler
private void timer1_Tick(object sender, EventArgs e)
{
    timerLabel.Text = DateTime.Now.ToString("HH:mm:ss, dddd");
}

Video_2023_04_13-2

When you set the Interval property to a positive integer value, the Timer starts counting down from that value in milliseconds. When the count reaches zero, the Timer raises the Tick event and resets the count to the Interval value, starting the countdown again.

To learn more on date & time formatting, visit here.

⬆ Back to top

Chapter 8 - Asynchronous SE

Asynchronous SE be like

Asynchronous programming in allows you to write code that can perform tasks without blocking the main thread, improving the responsiveness of your application.

Thread

In computing, a thread is the smallest unit of execution that can be scheduled by an operating system's scheduler.

In the context of asynchronous programming, threads are often used to execute tasks in parallel or to avoid blocking the main thread. For example, when you perform an I/O operation that might take some time to complete, such as reading a file from disk or downloading data from the internet, you can create a new thread to handle that operation while the main thread continues to run other tasks. [Docs]

threads are often used to execute tasks in parallel

FYI, the statement above refers to concurrency - the ability of a system to handle multiple tasks or processes at the same time.

Concurrency diagram

Creating a new thread

using System;
using System.Threading;

public class Program
{
    public static void MyThreadFunction()
    {
        Console.WriteLine("Thread is running...");
    }

    public static void Main()
    {
        Thread thread = new Thread(MyThreadFunction);
        thread.Start();
    }
}

.Join() & .Sleep() methods

.Join() - Block the calling thread until the thread being joined completes. This is often used to ensure that a thread has finished executing before continuing with the rest of the program. [Docs]

.Sleep() - Suspend the execution of the current thread for a specified amount of time. [Docs]

Below is the example of those function working together:

using System;
using System.Threading;

public class Program
{
    public static void MyThreadFunction()
    {
        Console.WriteLine("Thread is running...");
        Thread.Sleep(3000); // Simulate some work
        Console.WriteLine("Thread is done.");
    }

    public static void Main()
    {
        Thread thread = new Thread(MyThreadFunction);
        thread.Start();

        Console.WriteLine("Waiting for thread to complete...");
        thread.Join();

        Console.WriteLine("Thread has completed.");
    }
}

Thread Safety

Thread safety means ensuring that a program behaves correctly and predictably when multiple threads are executing at the same time.

The lock keyword in C# is used to prevent multiple threads from accessing the same code block simultaneously. It helps prevent problems that can occur when multiple threads try to access shared resources at the same time. [Docs]

This helps to prevent race conditions and other concurrency-related issues that can arise when multiple threads access shared resources.

Sharing global variables among threads

using System;
using System.Threading;

public class Program
{
    private static int count = 0;
    private static readonly object lockObject = new object();

    public static void IncrementCount()
    {
        lock (lockObject)
        {
            int temp = count;
            Thread.Sleep(100); // Simulate some work
            count = temp + 1;
        }
    }

    public static void Main()
    {
        Thread[] threads = new Thread[10];

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(IncrementCount);
            threads[i].Start();
        }

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i].Join();
        }

        Console.WriteLine("Final count: " + count);
    }
}

Sending signal among threads

The AutoResetEvent class represents a thread synchronization event that, when signaled, resets automatically after releasing a single waiting thread. [Docs]

class Program
{
    static AutoResetEvent autoEvent;

    static void Main(string[] args)
    {
        autoEvent = new AutoResetEvent(false);

        Console.WriteLine("Press any key to start the program");
        Console.ReadKey();

        new Thread(ThreadProcess).Start();

        // To wait for any signal
        while(true)
        {
            // Wait for the Signal
            autoEvent.WaitOne();

            // Signal is detected
            Console.WriteLine("Auto Reset Event is signalled");
        }
    }

    static void ThreadProcess()
    {
        Console.WriteLine("Thread Starting");
        Thread.Sleep(3000);

        // Signal is set
        autoEvent.Set();
    }
}

Thread pool

A thread pool is a collection of threads that can be used to perform several tasks in the background. It is a common technique used to optimize the performance of applications that use multiple threads to perform long-running asynchronous tasks. [Docs]

Docs and example code using Task class -> https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-7.0

Chapter 9 - Parallel Computing

parallel computing meme

Key concepts

  • Break down a problem into smaller tasks
  • Execute the tasks simultaneously on multiple processors
  • Combine the results of the tasks to get the final result
Parallel Computing

Parallel.For

Executes a for loop in which iterations may run in parallel. [Docs]

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        Parallel.For(0, 10, i =>
        {
            Console.WriteLine("i = " + i);
        });
    }
}

Parallel.ForEach

Executes a foreach operation on an IEnumerable in which iterations may run in parallel. [Docs]

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        string[] names = { "Alice", "Bob", "Charlie", "Dave" };

        Parallel.ForEach(names, name =>
        {
            Console.WriteLine("Hello " + name);
        });
    }
}

Confused between multi-threading (concurrency) and parallel computing?

HAhaha me too. Basically,

  • In multi-threading, a single process is used to divide the work into multiple threads (they divide the execution time).
  • In parallel computing, multiple processes are used to divide the work into multiple core (they divide the work).

concurrency vs parallel

Image credit: Baeldung

Great article to read on:

⬆ Back to top

Chapter 10 - Networking & Communication

Networking and communication are vital aspects of software engineering, facilitating seamless interaction and enabling the creation of interconnected software solutions.

Protocols

A protocol refers to a set of rules and guidelines that govern the exchange of data between devices or systems.

Protocol Description
TCP (Transmission Control Protocol) A reliable, connection-oriented protocol that ensures data integrity and sequencing during transmission.
UDP (User Datagram Protocol) A lightweight, connectionless protocol that provides fast, but unreliable data transmission without error checking or sequencing.
HTTP (Hypertext Transfer Protocol) A protocol that enables communication between web browsers and servers, facilitating the retrieval and display of web content.
FTP (File Transfer Protocol) A standard network protocol used for the transfer of files between a client and a server over a computer network.
SMTP (Simple Mail Transfer Protocol) A protocol for sending and receiving email messages between mail servers, enabling reliable email communication.

Some more examples: SSH, Telnet, TLS/SSL, IMAP etc.

TCP

In the OSI model, the TCP/IP protocol suite operates at the transport layer. The transport layer is responsible for providing data transfer services between two applications running on different hosts.

In C#, the TcpClient and TcpListener classes can be used to implement TCP communication between two applications.

Check out TCP Demo -> GUI or Console

TCP App screenshot

URI

Uniform Resource Identifier. It is a string of characters used to identify and locate resources on the internet. Example of URI schemes: irc://, mailto://, ftp://, http://, https://, file://, data:// etc.

IP Address

An Internet Protocol address is a numerical label assigned to each device connected to a computer network that uses the Internet Protocol for communication. [Wiki]

private vs public ip

Image credit: https://www.pragimtech.com/blog/azure/public-ip-address-vs-private-ip-address/

Local IP

aka an internal or private IP address, is used to identify devices within a local network. They are not publicly accessible from the internet and are only used for communication within the local network.

To view your local IP address, run ipconfig in the command prompt. [Example]

Public IP

A globally unique address assigned to a device connected to the internet. Public IP addresses are obtained from the Internet Service Provider (ISP) and can be accessed by other devices and servers on the internet.

To view your public IP address, visit https://myip.wtf (sorry, this site's language is a bit aggressive) [Example]

DNS

Domain Name System. It is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. Think of it like a phone book for the internet. [Wiki]

dns comic

Image credit: https://wizardzines.com/

⬆ Back to top

Chapter 11 - Serialization

Localhost diagram

~ refers to the process of converting complex data structures or objects into a format that can be stored or transmitted, and later reconstructed back into its original form. It allows data to be persisted in a file or transferred across different systems or platforms.

Type of Serialization

  • Binary: Data is converted into a binary format, which is more compact and efficient for storage and transmission. Binary serialization is often used when performance is a concern, such as in high-performance computing or network communication.

    In C#, BinaryFormatter from System.Runtime.Serialization.Formatters.Binary can be used to serialize and deserialize objects to and from binary format. Example code can be found here.

    But, I must warn you, don't use binary serilization. They have many limitations, such as you cannot deserialize a file that have been serialized from another project/assembly.

    BinaryFormatter is very brittle - and while you can often bang your head on the keyboard for 6 hours to get past each successive pain point, it really isn't worth it. [A wise guy from SO]

  • JSON (JavaScript Object Notation): A popular lightweight data interchange format that represents data as human-readable text. JSON serialization is widely used in web development and APIs for transmitting data between different systems or platforms.

    In C#, you can use third party packages such as JSON.NET to serialize and deserialize JSON files. Example code can be found here.

  • XML (eXtensible Markup Language): A markup language that defines a set of rules for encoding documents. XML serialization is commonly used for storing and exchanging structured data, especially when compatibility and extensibility are important.

    In C#, you can use XmlSerializer from System.Xml.Serialization namespace to serialize and deserialize XML files. For example code, see here

Serial Port

A serial port is a physical interface through which information transfers in or out one bit at a time. It is used to connect devices such as modems, printers, mice, and keyboards to a computer. [Wiki]

We can transfer data between PC and to Arduino using Serial Port. C# .NET has built in support for serial port communication. Check out my project Serilink - An Arduino Serial Monitor clone built with Winforms C# .NET.

Serilink

⬆ Back to top

Chapter 12 - Software Security

xkcd security

~ protects applications and systems from unauthorized access and malicious attacks. It involves identifying vulnerabilities, preventing breaches, and mitigating risks. By implementing robust security measures, organizations safeguard sensitive information, maintain system integrity, and ensure software availability.

Important security factors

Localhost diagram
Security Factor Description
Confidentiality Protects sensitive information from unauthorized access or disclosure.
Integrity Ensures the accuracy and reliability of data and systems, preventing unauthorized modifications.
Availability Ensures systems, networks, and services are accessible and operational when needed, minimizing downtime.
Authentication Verifies the identity of users or entities accessing a system, while authorization determines their privileges.

Security attacks

Passive attacks

Passive attacks aim to gather information without altering the target system or data. These attacks are often difficult to detect since they do not disrupt or modify the system.

Example: Network sniffing,

Active attacks

Active attacks involve actions that modify or disrupt the target system, data, or network. These attacks are more noticeable and can cause immediate harm or damage.

Example: Masquerade, denial-of-service (DDoS), Man-in-the-Middle (MITM), Modification

DDos attack traphic metaphor (Credit: Cloudflare): ddos cloudflare

Mapping attacks to security factors

  • Interruption: attack on Availability
  • Interception: attack on Confidentiality
  • Modification: attack on Integrity
  • Fabrication: attack on Authenticity

Cryptography

Caesar Cipher

Alkindi         Al-kindi-cryptanalysis

From "Risalah fi Istikhraj al-Mu'amma" (Treatise on Deciphering Cryptographic Messages), authored by Al-Kindi (Abu Yusuf Ya'qub ibn Ishaq al-Kindi) in 9th century, he mentioned about deciphering encrypted messages using frequency analysis [Source]:

One way to solve an encrypted message, if we know its language, is to find a different plaintext of the same language long enough to fill one sheet or so, and then we count the occurrences of each letter. We call the most frequently occurring letter the ‘first’, the next most occurring letter the ‘second’, the following most occurring the ‘third’, and so on, until we account for all the different letters in the plaintext sample

Then we look at the cipher text we want to solve and we also classify its symbols. We find the most occurring symbol and change it to the form of the ‘first’ letter of the plaintext sample, the next most common symbol is changed to the form of the ‘second’ letter, and so on, until we account for all symbols of the cryptogram we want to solve.

Al-Kindi recognized that certain letters or symbols in a language occur more frequently than others. By analyzing the frequency distribution of these characters in a ciphertext, he developed techniques to identify patterns and make educated guesses about the underlying plaintext.

Reproduction of al-Kindi's letter frequency table

Try Caesar Cipher yourself

Avalanche Effect

A slight change in either the key or the plain-text should result in a significant change in the cipher-text. It is considered as one of the desirable property of any encryption algorithm.

Encryption

~ is a process of converting plain or readable data, often referred to as plaintext, into an unintelligible form known as ciphertext. It is a fundamental technique used to protect sensitive information from unauthorized access or interception during storage or transmission. The encryption process involves using an encryption algorithm and a secret key to scramble the plaintext, making it unreadable to anyone who does not possess the corresponding decryption key.

flowchart LR
    A[Plaintext] --> B[Encryption Algorithm]
    B --> C[Ciphertext]
Loading

Symmetric encryption - Triple DES Encryption

Encryption using same secret key

Requirements

  • A strong encryption algorithm
  • A secret key only sender & receiver knows.

Triple DES Encryption (aka 3DES or TDES) applies DES algorithm three times to each data block using three different keys

triple DES

Image credit: Cisco

But it three times slower.

Asymmetric encryption - Advanced Encryption Standard (AES)

Encryption using public and private key

aes diagram

Image credit: wallarm

~ specifes a FIPS-approved cryptographic algorithm that can be used to protect electronic data. The algorithm is a symmetric block cipher that can encrypt (encipher) and decrypt (decipher) digital information.

The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt and decrypt data in blocks of 128 bits.

Read the publication: https://doi.org/10.6028/NIST.FIPS.197-upd1

Another example: When connecting to cloud VM, you'll need to SSH to that machine using the private key generated. Read more on: https://iqfareez.com/blog/deploy-nodered-flows-to-the-cloud-azure#accessing-your-vms-shell

RSA

RSA

Image credit: MindMajix

Named after its inventors, Rivest–Shamir–Adleman.

~ relies on a public key and a private key. The keys are mathematically related, but it is computationally infeasible to derive the private key from the public key.

Hashing

~ refers to the process of taking an input (or key) and applying a mathematical function called a hash function to produce a fixed-size output.

Hashing diagram

Hashing is one way function - once you hash, you cannot de-hash.

Application of hashing

  • Data Retrieval: Hash functions are used to map keys to indexes in an array, allowing efficient storage and retrieval of data.
  • Password Storage: Rather than storing actual passwords, systems typically store the hash values of passwords. When a user enters their password, it is hashed and compared to the stored hash value.
  • Digital Signatures: Provides a way to verify the integrity and authenticity of digital messages or documents.
  • Data Integrity: Hashing can be used to ensure data integrity during transmission or storage. By calculating the hash value of a file or message before and after transmission, one can verify if the data has been modified during transit or if any errors have occurred. This technique is commonly used in checksum algorithms and integrity checking mechanisms.
  • Cryptographic Hash Functions: Specifically designed to have properties like collision resistance and preimage resistance. Collision resistance means that it is computationally infeasible to find two different inputs that produce the same hash value.

Learn more:

⬆ Back to top

About

Software Engineering | IIUM | Mechatronics

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages