1801 words
9 minutes
C# Concepts Every Developer Should Know - Part 1

Introduction#

As a .NET developer, I have noticed that there are very important concepts that help us up-level our coding skills. This article is the first in the Dotnet-Foundation series that I will be sharing with you. In this series, I will focus on introducing various concepts — some will be theoretical, some will include illustrative code, or references to original documentation to help you easily grasp these critically important concepts. In the future, I plan to move toward a System-Design In Practice series, and it will definitely be based on real-world scenarios where we will apply these theories and concepts extensively. But first, let’s explore the concepts that every C#/.NET developer should know!

1. Primitive Data Types in C##

Primitive data types are built-in types in the C# language, used to store simple values such as integers (int), floating-point numbers (float, double), characters (char), and boolean values (bool). These form the foundation for building more complex structures.

  • Integers (int, long, short): Stored on the stack, providing fast access and no memory fragmentation.
  • Floating-point numbers (float, double, decimal): Commonly used for calculations involving real numbers, but decimal is preferred for financial processing due to its higher precision.
  • Boolean (bool): Stores true or false values, commonly used in conditional structures.
  • Char: Represents a single Unicode character, stored as 16-bit.
using System;

class CircleAreaCalculator
{
    static void Main()
    {
        const double Pi = 3.14159; // Hằng số Pi
        double radius;

        Console.WriteLine("Nhập bán kính hình tròn:");
        radius = Convert.ToDouble(Console.ReadLine());

        double area = Pi * radius * radius;
        Console.WriteLine($"Diện tích hình tròn là: {area}");
    }
}

Notes:

  • The stack operates more efficiently than the heap, so value types are generally processed faster than reference types.
  • Choosing the correct data type will make your code more solid and can also have a positive impact on performance.

2. Data Types and Variables in C##

Variables in C# are temporary storage locations in memory. Understanding how to declare and use data types properly helps optimize performance and reduce errors.

  • There are two main types of variables:
    • Value Type: Stores the value directly.
    • Reference Type: Stores a reference address pointing to the value on the heap.
  • Variables can be declared with the var keyword for automatic type inference.
using System;

class TypeConversionExample
{
    static void Main()
    {
        string numberStr = "42";
        int numberInt = int.Parse(numberStr); // Ép kiểu tường minh
        double numberDouble = numberInt; // Ép kiểu ngầm định

        Console.WriteLine($"Số nguyên: {numberInt}");
        Console.WriteLine($"Số thực: {numberDouble}");
    }
}

Notes:

  • Implicit casting does not incur additional processing overhead.
  • Explicit casting can cause runtime errors if not checked beforehand.
  • Using var may make code harder to read, but does not affect performance.

Best practices:

  • Declare variables with the narrowest scope possible to reduce memory overhead.
  • Avoid using var when the data type is not obvious, to improve code readability.
  • Check data types before casting, especially with incompatible types.
  • Use local variables instead of global variables to reduce resource consumption.

3. Operators and Expressions in C##

Operators are symbols used to perform operations on variables and values.

  • Arithmetic operators: +, -, *, /, % are used for basic mathematical operations.
  • Comparison operators: ==, !=, <, > check the relationship between two values.
  • Logical operators: &&, ||, ! are used to combine conditions.
  • Ternary operator: (condition) ? trueResult : falseResult is a shorthand for if-else structures.
using System;

class OddEvenChecker
{
    static void Main()
    {
        Console.WriteLine("Nhập một số:");
        int number = int.Parse(Console.ReadLine());

        string result = (number % 2 == 0) ? "chẵn" : "lẻ";
        Console.WriteLine($"Số {number} là số {result}.");
    }
}

Notes:

  • Using arithmetic operators like % can be slower than bitwise checks in certain specific cases.
  • Short-circuit logical operators like &&, || improve performance by stopping evaluation when unnecessary.

Best practices:

  • Prefer short-circuit operators (&&, ||) to reduce unnecessary evaluations.
  • Avoid complex expressions in conditions to improve clarity and performance.
  • Use the checked operator to detect overflow in arithmetic operations.

4. Control Structures (if-else, loops)#

Control structures allow code execution based on conditions or repeated execution of code blocks.

  • Conditional structures (if-else): Direct program flow.
  • Loops (for, while, do-while): Repeat code blocks based on conditions.
  • foreach loop: Iterate through elements in a collection.
using System;

class OddSumCalculator
{
    static void Main()
    {
        int sum = 0;
        for (int i = 1; i <= 10; i++)
        {
            if (i % 2 != 0)
                sum += i;
        }
        Console.WriteLine($"Tổng các số lẻ từ 1 đến 10 là: {sum}");
    }
}

Notes:

  • The for loop is faster than foreach when working with arrays, as it avoids the iterator overhead.
  • break and continue help reduce unnecessary iterations, improving performance.

Best practices:

  • Use break to exit loops when a condition is met.
  • Avoid deeply nested loops; use more optimal algorithms instead.
  • When iterating over large collections, consider using Parallel.For to improve performance.

5. Methods and Functions#

Methods are blocks of code that perform a specific task and can be called from multiple places in the program, enabling code reuse and better organization.

  • Methods with return values: Use the return keyword to return a value.
  • Methods without return values: Use the void keyword.
  • Parameters:
    • Required parameters: Must provide a value when calling the function.
    • Optional parameters: Have default values.
  • Recursion: A method that calls itself.
using System;

class FactorialCalculator
{
    static int Factorial(int n)
    {
        if (n == 0 || n == 1) return 1;
        return n * Factorial(n - 1);
    }

    static void Main()
    {
        Console.Write("Nhập số cần tính giai thừa: ");
        int num = int.Parse(Console.ReadLine());
        Console.WriteLine($"Giai thừa của {num} là: {Factorial(num)}");
    }
}

Notes:

  • Recursion uses the stack to store function state, which can lead to stack overflow errors if not properly controlled.
  • Methods with optional parameters reduce the number of overloads needed, making code more concise.

Best practices:

  • Avoid using recursion if it can be replaced with a loop to prevent stack overflow errors.
  • Use async and await when handling asynchronous methods.
  • Name methods clearly, accurately describing their task.

6. Classes and Objects#

A class is a blueprint for objects, while an object is an instance of a class. This is the foundation of object-oriented programming.

  • A class consists of:
    • Properties: Used to store data.
    • Methods: Used to manipulate data.
  • Objects: Created from classes using the new keyword.
  • Encapsulation: Restricts direct access to data through private and public access modifiers.
using System;

class Car
{
    public string Model { get; set; }
    public string Color { get; set; }

    public void Start()
    {
        Console.WriteLine($"{Color} {Model} đang khởi động.");
    }
}

class Program
{
    static void Main()
    {
        Car myCar = new Car { Model = "Toyota", Color = "Xanh" };
        myCar.Start();
    }
}

Notes:

  • Objects created on the heap need to be garbage collected, leading to higher memory management costs.
  • Encapsulation improves security and reduces errors, but may increase data access overhead.

Best practices:

  • Use encapsulation (private, public, protected) to protect data.
  • Use the readonly keyword for read-only properties.
  • Avoid creating unnecessary objects to reduce pressure on the garbage collector.

7. Inheritance and Polymorphism#

Inheritance allows a child class to inherit properties and methods from a parent class. Polymorphism allows an object to take on many different forms.

  • Inheritance: Child classes use the : symbol to extend parent classes.
  • Polymorphism:
    • Virtual methods: Can be overridden by child classes.
    • Abstract methods: Must be implemented by child classes.
using System;

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Động vật kêu.");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Chó sủa: Gâu gâu!");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog();
        myDog.Speak(); // Gọi phương thức ghi đè của lớp Dog
    }
}

Notes:

  • Virtual and override methods require an additional lookup table (vtable), making them slower than non-virtual methods.
  • Improper use of inheritance can increase complexity.

Best practices:

  • Use sealed to prevent child classes from inheriting a class when unnecessary.
  • Use polymorphism when flexible extensibility is needed; avoid overriding when not necessary.
  • Do not overuse inheritance; consider using composition instead.

8. Interfaces and Abstract Classes#

Interfaces define behaviors that a class must implement. Abstract classes provide a common framework and can contain default logic.

  • Interface:
    • Only defines methods, no logic.
    • A class can implement multiple interfaces.
  • Abstract class:
    • Can contain abstract methods or pre-built logic.
    • A child class can only inherit from one abstract class.
using System;

interface ILogger
{
    void Log(string message);
}

class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }
}

class Program
{
    static void Main()
    {
        ILogger logger = new ConsoleLogger();
        logger.Log("Hello, World!");
    }
}

Notes:

  • Calling methods through an interface has higher overhead compared to direct calls.
  • Abstract classes can use default logic, reducing code duplication.

Best practices:

  • Use interfaces to increase flexibility and extensibility.
  • Use abstract classes when shared logic is needed for child classes.
  • Avoid overusing interfaces or creating too many unnecessary abstract classes.

9. Exception Handling#

Exception handling in C# helps programs handle errors safely and avoid unexpected termination.

  • An Exception is an error that occurs while the program is running, such as division by zero or accessing an array out of bounds.
    • The try-catch-finally block:
    • try: Contains code that may generate errors.
    • catch: Handles errors if they occur.
    • finally: Executes code regardless of whether an error occurred.
  • Creating custom exceptions: Inherit from the Exception class.
using System;

class ExceptionHandlingExample
{
    static void Main()
    {
        try
        {
            Console.Write("Nhập số bị chia: ");
            int dividend = int.Parse(Console.ReadLine());
            Console.Write("Nhập số chia: ");
            int divisor = int.Parse(Console.ReadLine());

            int result = dividend / divisor;
            Console.WriteLine($"Kết quả: {result}");
        }
        catch (DivideByZeroException)
        {
            Console.WriteLine("Lỗi: Không thể chia cho 0.");
        }
        catch (FormatException)
        {
            Console.WriteLine("Lỗi: Vui lòng nhập số hợp lệ.");
        }
        finally
        {
            Console.WriteLine("Cảm ơn bạn đã sử dụng chương trình.");
        }
    }
}

Notes:

  • Throwing and catching exceptions is CPU-expensive. Avoid using exceptions for regular condition checking.
  • Using many catch blocks increases complexity and can affect performance.

Best practices:

  • Only use exceptions for unavoidable errors.
  • Always use finally to release resources.
  • Log detailed error information for easier troubleshooting.

10. Arrays and Lists#

Arrays and Lists are fundamental data structures for storing collections of elements in C#.

  • Array:
    • Fixed size, stores elements of the same data type.
    • Fast access by index.
  • List:
    • Dynamic size, flexible for adding/removing elements.
    • Supports many LINQ methods.
using System;
using System.Collections.Generic;

class ArrayAndListExample
{
    static void Main()
    {
        // Mảng
        int[] array = { 1, 2, 3, 4, 5 };
        int arraySum = 0;
        foreach (var item in array)
        {
            arraySum += item;
        }
        Console.WriteLine($"Tổng mảng: {arraySum}");

        // Danh sách
        List<int> list = new List<int> { 1, 2, 3, 4, 5 };
        list.Add(6); // Thêm phần tử
        int listSum = 0;
        foreach (var item in list)
        {
            listSum += item;
        }
        Console.WriteLine($"Tổng danh sách: {listSum}");
    }
}

Notes:

  • Array: Faster access than lists since it does not need to handle dynamic structure overhead.
  • List: Consumes more memory when resizing during element additions. Best practices:
  • Use arrays when the data size is known in advance.
  • Use Listwhen flexible adding/removing of elements is needed.
  • Avoid resizing arrays or lists multiple times to reduce memory overhead.

Conclusion#

In this article, I have introduced 10 concepts that I consider fundamental and that every developer should understand. Of course, the scope of this article is introductory, and the theory may be a bit dry. But trust me, mastering any discipline requires a solid grasp of theory combined with practice — practicing over and over again is the only way to truly succeed. In the next article, I will continue to introduce 10 more concepts, and I think they will be a bit more advanced. Stay tuned. Thanks for reading!

C# Concepts Every Developer Should Know - Part 1
https://www.devwithshawn.com/en/posts/concepts-should-know-part1-en/
Author
PDXuan(Shawn)
Published at
2024-05-01
Share: