- Chapter 1 - Introduction
- Chapter 2 - Software Engineering Principles
- Chapter 3 - Primitive Data Types
- Chapter 4 - Object Oriented Programming
- Chapter 5 - Event Driven SE
- Chapter 6 - Data Structures
- Chapter 7 - Graphical User Interface (GUI)
- Chapter 8 - Asynchronous Software Engineering
- Chapter 9 - Parallel Computing
- Chapter 10 - Networking & Communication
- Chapter 11 - Serialization
- Chapter 12 - Software Security
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.
Modern programming languages, like C#, Python, Dart etc, are built on top of lower level programming languages.
- 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.
- 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# 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).
- 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.
- 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.
mindmap
root((Software))
Sofware need to be engineered
Software doesn't wear out, but detoriate over time
Software is complex
~ 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.
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
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.
aka Software Development Methodology
A process for planning, creating, testing, and deploying an application or system.
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
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. |
The process of identifying, documenting, and managing requirements for a product, system, or software to satisfy customer needs and business objectives.
- Inception - Asks basic questions about the project, such as what the project is, why it is needed, and who will use it.
- Elicitation - Address problems of scope/understanding/volatility.
- Elaboration - Analysis model that identifies data, function, features, constraints and behavioral requirements.
- Negotiation - Agree on a deliverable system
- Specification
UML (Unified Modeling Language) diagrams that are commonly used in software engineering to help design and document software systems.
Some of the examples including:
Below are some examples:
int
- 32-bit signed integerdouble
- 64-bit floating point numberchar
- 16-bit Unicode characterbool
- Boolean value
Read more on docs
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]
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.
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
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};
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]);
}
The foreach
loop is used to iterate through the elements of a collection. [Docs]
foreach (int i in foo)
{
Console.WriteLine(i);
}
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.
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 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? 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]
private static void PrintHello()
{
Console.WriteLine("Hello world");
}
private static int AddNumber(int a, int b)
{
return a + b;
}
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
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.
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
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
Usually from literal string to a specific type. Eg: string ➡️ int
int.Parse("123"); // 123
int arg1 = int.Parse(args[0]);
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.
}
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
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.
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.
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 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.
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);
}
}
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
}
~ 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.
}
}
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.");
}
}
To prevent a class from being inherited, use the sealed
keyword. [Docs]
sealed class Animal
{
// implementation
}
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 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
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();
}
}
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 | 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. |
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 theType
of the current instance.ToString()
- Returns a string that represents the current object.
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
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.
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>
orDictionary<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.
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
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]
Note - The built-in delegate can have up to 16 input parameters.
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
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.
An event is a notification sent by an object to signal the occurrence of an action. [Docs]
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
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();
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);
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;
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
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"
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"}
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" }
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" }
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
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
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
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
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
[Source]
~ 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 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]
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);
}
answerLabel.Text = "Answer: " + output.ToString();
var myForm = new Form2();
myForm.Show();
for (var i = 0; i < lines.Length; i += 1)
{
var data = lines[i].Split(',');
dataGridView1.Rows.Add(data);
}
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");
}
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.
Asynchronous programming in allows you to write code that can perform tasks without blocking the main thread, improving the responsiveness of your application.
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.
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()
- 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 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.
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);
}
}
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();
}
}
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
- 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
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);
});
}
}
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);
});
}
}
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).
Image credit: Baeldung
Great article to read on:
- https://www.baeldung.com/cs/concurrency-vs-parallelism
- https://www.loginradius.com/blog/engineering/concurrency-vs-parallelism/
Networking and communication are vital aspects of software engineering, facilitating seamless interaction and enabling the creation of interconnected software solutions.
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.
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
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.
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]
Image credit: https://www.pragimtech.com/blog/azure/public-ip-address-vs-private-ip-address/
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]
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]
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]
Image credit: https://wizardzines.com/
~ 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.
-
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
fromSystem.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
fromSystem.Xml.Serialization
namespace to serialize and deserialize XML files. For example code, see here
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.
~ 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.
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. |
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 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):
- Interruption: attack on Availability
- Interception: attack on Confidentiality
- Modification: attack on Integrity
- Fabrication: attack on Authenticity
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.
- https://cryptii.com/pipes/caesar-cipher
- Bruteforce decryption: https://www.boxentriq.com/code-breaking/caesar-cipher
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.
~ 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]
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
Image credit: Cisco
But it three times slower.
Encryption using public and private key
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
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.
~ 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 is one way function - once you hash, you cannot de-hash.
- 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: