Casting

Casting truncates the value.

decimal myDecimal = 3.14m;
Console.WriteLine($"decimal: {myDecimal}"); // decimal: 3.14

int myInt = (int)myDecimal;
Console.WriteLine($"int: {myInt}"); // int: 3
To String
int first = 5;
int second = 7;
string message = first.ToString() + second.ToString();
Console.WriteLine(message); // 57
Parse
string first = "5";
string second = "7";
int sum = int.Parse(first) + int.Parse(second);
Console.WriteLine(sum); // 12
Parse
string value = "102";
int result = 0;
if (int.TryParse(value, out result))
{
   Console.WriteLine($"Measurement: {result}");
}
else
{
   Console.WriteLine("Unable to report the measurement.");
}
Convert

Convert rounds the value.

string value1 = "5";
string value2 = "7";
int result = Convert.ToInt32(value1) * Convert.ToInt32(value2);
Console.WriteLine(result); // 35
Content
  1. Rules of functions

  2. Resources

Objects and Data Structures

Data Abstraction

Abstraction is the process of hiding implementation details and exposing only the essential features of a system or component. As well, using proper names that don’t expose the implementation details.

Data/Object Anti-Symmetry

  • Objects hide their data behind abstractions and expose functions that operate on that data.
  • Data structure expose their data and have no meaningful functions.
// Shared class
public class Point {
  public double x;
  public double y;
}

// Procedural Code
public class Square {
  public Point topLeft;
  public double side;
}
public class Rectangle {
  public Point topLeft;
  public double height;
  public double width;
}
public class Circle {
  public Point center;
  public double radius;
}
public class Geometry {
  public double PI = 3.141592653589793;
  public double area(Object shape)
  {
    if (shape is Square) {
      Square s = (Square)shape;
      return s.side * s.side;
    }
    else if (shape is Rectangle) {
      Rectangle r = (Rectangle)shape;
      return r.height * r.width;
    }
    else if (shape is Circle) {
      Circle c = (Circle)shape;
      return PI * c.radius * c.radius;
    }
    else 
    {
      return 0;
    }
  }
}

// OO Code
public interface Shape
{
  double area();
}

public class Square : Shape {
  private Point topLeft;
  private double side;
  public double area() {
    return side*side;
  }
}
public class Rectangle : Shape {
  private Point topLeft;
  private double height;
  private double width;
  public double area() {
    return height * width;
  }
}
public class Circle : Shape {
  private Point center;
  private double radius;
  public double PI = 3.141592653589793;
  public double area() {
    return PI * radius * radius;
  }
}
  • Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.
  • Procedural code makes it hard to add new data structures because all the functions must change. OO code makes it hard to add new functions because all the classes must change.
The Law of Demeter

There is a well-known heuristic called the Law of Demeter that says a module should not know about the innards of the objects it manipulates.

More precisely, the Law of Demeter says that a method f of a class C should only call the methods of these:

  • C (Same class)
  • An object created by f (Inside the same method)
  • An object passed as an argument to f
  • An object held in an instance variable of C

Violation of the Law of Demeter

  • Train Wrecks: In objects, avoid excessive chaining of method calls. It is usually best to split them up.
  • Hybrids: This confusion sometimes leads to unfortunate hybrid structures that are half object and half data structure. Such hybrids make it hard to add new functions but also make it hard to add new data structures.
  • Hiding Structure: Encapsulate implementation details.

Data Transfer Objects (DTO)

Data Transfer Objects (DTOs) are objects used to transfer data between different layers or components of a software system. DTOs are very useful structures, especially when communicating with databases or parsing messages from sockets, and so on. They often become the first in a series of translation stages that convert raw data in a database into objects in the application code.

Active Records are special format of DTOs as they have navigational methods like save and find.

public class Address {
  private string Street { get; };
  private string StreetExtra { get; };
  private string City { get; };
  private string State { get; };
  private string Zip { get; };
  
  public Address(
    string street,
    string streetExtra,
    string city,
    string state,
    string zip)
  {
    Street = street;
    StreetExtra = streetExtra;
    City = city;
    State = state;
    Zip = zip;
  }
}
Active Record

Active Records are special forms of DTOs. They are data structures with public (or beanaccessed) variables; but they typically have navigational methods like save and find. Typically these Active Records are direct translations from database tables, or other data sources.

Resources
Sort
string[] pallets = [ "B14", "A11", "B12", "A13" ];

Console.WriteLine("Sorted...");
Array.Sort(pallets);
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // A11, A13, B12, B14
}
Reverse
string[] pallets = [ "A11", "A13", "B12", "B14" ];

Console.WriteLine("Reversed...");
Array.Reverse(pallets);
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // B14, B12, A13, A11
}
Clear
string[] pallets = [ "B14", "A11", "B12", "A13" ];
Console.WriteLine("");

Array.Clear(pallets, 0, 2);
Console.WriteLine($"Clearing 2 ... count: {pallets.Length}");
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // null, null, B12, A13
}
Resize
string[] pallets =  ["B14", "A11", "B12", "A13" ];
Console.WriteLine("");

Array.Resize(ref pallets, 6);
Console.WriteLine($"Resizing 6 ... count: {pallets.Length}");

pallets[4] = "C01";
pallets[5] = "C02";

foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // B14, A11, B12, A13, C01, C02
}
Join
char[] valueArray = ['a', 'b', 'c']
Array.Reverse(valueArray);
// string result = new string(valueArray);
string result = String.Join("|", valueArray); // a|b|c
Console.WriteLine(result);
Split
string result = "123|456|789";
string[] items = result.Split('|');
foreach (string item in items)
{
   Console.WriteLine(item); // 123, 456, 789
}
Composite Formatting
string first = "Hello";
string second = "World";
string result = string.Format("{0} {1}!", first, second);
Console.WriteLine(result); // Hello World!
Formatting Currency
decimal price = 123.45m;
int discount = 50;
Console.WriteLine($"Price: {price:C} (Save {discount:C})"); // Price: $123.45 (Save $50.00)
Formatting Numbers
decimal measurement = 123456.78912m;
Console.WriteLine($"Measurement: {measurement:N} units"); // Measurement: 123,456.79 units
Console.WriteLine($"Measurement: {measurement:N4} units"); // Measurement: 123,456.7891 units
Formatting Percentage
decimal tax = .36785m;
Console.WriteLine($"Tax rate: {tax:P2}"); // Tax rate: 36.79%
Padding
string input = "Pad this";
Console.WriteLine(input.PadLeft(12)); // " 	Pad this"
Console.WriteLine(input.PadLeft(12, '*')); // "****Pad this"
Console.WriteLine(input.PadRight(12, '*')); // "Pad this****"
IndexOf
string message = "Find what is (inside the parentheses)";

int openingPosition = message.IndexOf('(');
int closingPosition = message.IndexOf(')');

Console.WriteLine(openingPosition); // 13
Console.WriteLine(closingPosition); // 36
Substring
string message = "What is the value <span>between the tags</span>?";

const string openSpan = "<span>";
const string closeSpan = "</span>";

int openingPosition = message.IndexOf(openSpan);
int closingPosition = message.IndexOf(closeSpan);

openingPosition += openSpan.Length;
int length = closingPosition - openingPosition;
Console.WriteLine(message.Substring(openingPosition, length)); // between the tags
LastIndexOf
string message = "hello there!";

int first_h = message.IndexOf('h');
int last_h = message.LastIndexOf('h');
Console.WriteLine($"For the message: '{message}', the first 'h' is at position {first_h} and the last 'h' is at position {last_h}."); 
// For the message: 'hello there!', the first 'h' is at position 0 and the last 'h' is at position 7.
IndexOfAny
string message = "Hello, world!";
char[] charsToFind = { 'a', 'e', 'i' };

int index = message.IndexOfAny(charsToFind);

Console.WriteLine($"Found '{message[index]}' in '{message}' at index: {index}."); // Found 'e' in 'Hello, world!' at index: 1.
Remove
string data = "12345John Smith          5000  3  ";
string updatedData = data.Remove(5, 20);
Console.WriteLine(updatedData); // 123455000  3  
Replace
string message = "This--is--ex-amp-le--da-ta";
message = message.Replace("--", " ");
message = message.Replace("-", "");
Console.WriteLine(message); // This is example data
Trim
string greeting = "      Hello World!       ";
Console.WriteLine($"[{greeting}]"); // "      Hello World!       "

string trimmedGreeting = greeting.TrimStart();
Console.WriteLine($"[{trimmedGreeting}]"); // "Hello World!       "

trimmedGreeting = greeting.TrimEnd();
Console.WriteLine($"[{trimmedGreeting}]"); // "      Hello World!"

trimmedGreeting = greeting.Trim();
Console.WriteLine($"[{trimmedGreeting}]"); // "Hello World!"
Contains
string songLyrics = "You say goodbye, and I say hello";
Console.WriteLine(songLyrics.Contains("goodbye")); // True
Console.WriteLine(songLyrics.Contains("greetings")); // False
Declaring Classes
// A type that is defined as a class is a reference type.

//[access modifier] - [class] - [identifier]
public class Customer
{
  // Fields, properties, methods and events go here...
}
Creating Objects
Customer object1 = new Customer(); // object1 is a reference to an allocated space that will know where the object exists.
Customer object2; // Reference to null

Customer object3 = new Customer(); 
Customer object4 = object3; // object4 has the same reference as object3. If any of both instances changes, the other one does as well. Not recommended to do that.
Constructors and initialization

Accept default values

Every .NET type has a default value. Typically, that value is 0 for number types, and null for all reference types. You can rely on that default value when it’s reasonable in your app.

Field initializers

public class Container
{
  private int _capacity;

  public Container(int capacity) => _capacity = capacity;
}

Primary Constructor

public class Container(int capacity)
{
  private int _capacity = capacity;
}

Object initializer

public class Person
{
  public required string LastName { get; set; }
  public required string FirstName { get; set; }
}

var p1 = new Person(); // Error! Required properties not set
var p2 = new Person() { FirstName = "Grace", LastName = "Hopper" };
Example 1
abstract class Shape
{
  public abstract int GetArea();
}

class Square : Shape
{
  private int _side;

  public Square(int n) => _side = n;

  // GetArea method is required to avoid a compile-time error.
  public override int GetArea() => _side * _side;

  static void Main()
  {
    var sq = new Square(12);
    Console.WriteLine($"Area of the square = {sq.GetArea()}");
  }
}
// Output: Area of the square = 144
Example 2
// Abstract class
abstract class BaseClass
{
  protected int _x = 100;
  protected int _y = 150;

  // Abstract method
  public abstract void AbstractMethod();

  // Abstract properties
  public abstract int X { get; }
  public abstract int Y { get; }
}

class DerivedClass : BaseClass
{
  public override void AbstractMethod()
  {
    _x++;
    _y++;
  }

  public override int X   // overriding property
  {
    get
    {
      return _x + 10;
    }
  }

  public override int Y   // overriding property
  {
    get
    {
      return _y + 10;
    }
  }

  static void Main()
  {
    var o = new DerivedClass();
    o.AbstractMethod();
    Console.WriteLine($"x = {o.X}, y = {o.Y}");
  }
}
// Output: x = 111, y = 161
Example 3
public abstract class Shape
{
  public string Color { get; set; }

  // Constructor of the abstract class
  protected Shape(string color)
  {
    Color = color;
    Console.WriteLine($"Created a shape with color {color}.");
  }

  // Abstract method that must be implemented by derived classes
  public abstract double CalculateArea();
}

public class Square : Shape
{
  public double Side { get; set; }

  // Constructor of the derived class calling the base class constructor
  public Square(string color, double side) : base(color)
  {
    Side = side;
  }

  public override double CalculateArea()
  {
    return Side * Side;
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Square square = new Square("red", 5);
    Console.WriteLine($"Area of the square: {square.CalculateArea()}");            
  }
}
Definition

A record in C# is a class or struct that provides special syntax and behavior for working with data models. The record modifier instructs the compiler to synthesize members that are useful for types whose primary role is storing data. These members include an overload of ToString() and members that support value equality.

When to use

Consider using a record in place of a class or struct in the following scenarios:

  • You want to define a data model that depends on value equality.
  • You want to define a type for which objects are immutable.
Record Types

Declaration:

 // Value type: Stores all values
public record Point(int X, int Y)
{
  public double Slope() => (double)Y / (double)X;
}

public static void Main()
{
  Point pt = new Point(1, 1);
  var pt2 = pt with { Y = 10 };
  double slope = pt.Slope();

  Console.WriteLine($"The two points are {pt} and {pt2}"); // The two points are Point { X = 1, Y = 1 } and Point { X = 1, Y = 10 }

  Console.WriteLine($"The slope of {pt} is {slope}"); //The slope of Point { X = 1, Y = 1 } is 1
}
Declaration
interface IEquatable<T>
{
  bool Equals(T obj);
}

public class Car : IEquatable<Car>
{
  public string? Make { get; set; }
  public string? Model { get; set; }
  public string? Year { get; set; }

  // Implementation of IEquatable<T> interface
  public bool Equals(Car? car)
  {
    return (this.Make, this.Model, this.Year) ==
      (car?.Make, car?.Model, car?.Year);
  }
}
Declaration
// Declare the generic class.
public class GenericList<T>
{
  public void Add(T item) { }
}

public class ExampleClass { }

class TestGenericList
{
  static void Main()
  {
    // Create a list of type int.
    GenericList<int> list1 = new();
    list1.Add(1);

    // Create a list of type string.
    GenericList<string> list2 = new();
    list2.Add("");

    // Create a list of type ExampleClass.
    GenericList<ExampleClass> list3 = new();
    list3.Add(new ExampleClass());
  }
}