Loading...

Class, Object, Property and Methods in C#

Learn how classes, objects, properties, and methods work in C# and form the core building blocks of object-oriented programming.

In Object-Oriented Programming (OOP), a class is a template that gathers the data (properties) and behaviors (methods) of a particular concept under one structure. Instances created from this template are called objects. Properties are used for data access, while methods are used to define behaviors.


What is a Class?

A class is a template used to model a concept or entity in software. It defines properties, fields, and methods. For example, a Product class can store data like the product’s name and price, while also including behaviors such as “print information”.


public class Product
{
    private decimal _price;

    public string Name { get; set; } = string.Empty;

    public decimal Price
    {
        get => _price;
        set => _price = value < 0 ? 0 : value; // negative price prevented
    }

    public void PrintInfo()
    {
        Console.WriteLine($"{Name} - {Price:0.00} USD");
    }
}

Creating an Object

To create a real instance (object) from a class, the new keyword is used. This operation creates an object in memory according to the class definition. Properties can be assigned, and methods can be invoked on the object.


var notebook = new Product();
notebook.Name = "Notebook";
notebook.Price = 45m;
notebook.PrintInfo();
// Output:
Notebook - 45.00 USD

Objects can also be initialized more concisely using an object initializer:


var pen = new Product { Name = "Pen", Price = 12.5m };
pen.PrintInfo();

Property Types

Properties provide controlled access to the data inside a class. Instead of directly exposing fields, using properties allows for validation and business rules to be applied.

Auto-properties: The most practical choice when no extra logic is needed.


public class Customer
{
    public int Id { get; set; }
    public string FullName { get; set; } = string.Empty;
}

Property with a backing field: Provides control through a private field.


public class Temperature
{
    private double _celsius;

    public double Celsius
    {
        get => _celsius;
        set => _celsius = value;
    }

    public double Fahrenheit => (_celsius * 9 / 5) + 32; // read-only
}

Init-only property (C# 9+): Can only be set during object initialization, not afterwards.


public class User
{
    public string Username { get; init; } = string.Empty;
    public string Email { get; init; } = string.Empty;
}

var u = new User { Username = "john", Email = "j@example.com" };
// u.Username = "michael"; // error: init-only property

Methods

Methods define the operations an object can perform. They may accept parameters and return values. Multiple methods with the same name but different signatures can be defined (overloading).


public class MathUtil
{
    public static int Sum(int a, int b) => a + b;
    public static int Sum(int a, int b, int c) => a + b + c; // overload
}

int x = MathUtil.Sum(2, 3);     // 5
int y = MathUtil.Sum(1, 2, 3);  // 6

Instance methods are called on an object, while static methods are called directly on the class.


Access Modifiers

In C#, the visibility of classes and their members is controlled by access modifiers such as public, private, protected, and internal:

Access modifiers define from which parts of the program a class or its members (properties, methods, fields) can be accessed. For example, private members are only accessible within the class they are defined in, preventing external interference and ensuring data safety. public members are accessible from anywhere, representing the controlled interface a class exposes to the outside world. protected members are accessible in the class itself and in derived classes, allowing flexibility in inheritance scenarios. internal members are accessible within the same assembly, but not from other projects. This mechanism answers the question “which information should be exposed and which should stay encapsulated,” helping to reduce dependencies between classes, apply the encapsulation principle, and build a secure, understandable architecture.


public class Account
{
    private decimal _balance; // accessible only inside the class

    public decimal Balance { get; private set; } // read-only outside

    protected string Owner { get; set; } = string.Empty; // usable in derived classes

    internal string? Tag { get; set; } // accessible within the same project

    public void Deposit(decimal amount)
    {
        if (amount <= 0) return;
        Balance += amount;
    }
}

Example: Simple Library System

In the following example, a Book class and a managing LibraryService class are defined. This demonstrates how objects are created, stored, and processed.


public class Book
{
    public int Id { get; init; }
    public string Title { get; set; } = string.Empty;
    public string Author { get; set; } = string.Empty;

    public void Print() => Console.WriteLine($"{Id} - {Title} ({Author})");
}

public class LibraryService
{
    private readonly List<Book> _books = new();

    public void Add(Book b) => _books.Add(b);

    public Book? FindById(int id) => _books.FirstOrDefault(b => b.Id == id);

    public void PrintAll()
    {
        if (_books.Count == 0)
        {
            Console.WriteLine("Library is empty.");
            return;
        }
        foreach (var b in _books) b.Print();
    }
}

// Usage
var lib = new LibraryService();
lib.Add(new Book { Id = 1, Title = "Clean Code", Author = "Robert C. Martin" });
lib.Add(new Book { Id = 2, Title = "CLR via C#", Author = "Jeffrey Richter" });
lib.PrintAll();

TL;DR

  • Class: A template defining data + behavior.
  • Object: A concrete instance created from a class.
  • Property: Provides controlled access to data (encapsulation).
  • Method: Defines what an object can do (instance or static).
  • Access modifiers: Visibility controlled by public, private, protected, internal.

Example: Company Information and Validation

In this example, the Company class not only stores user input but also validates it with a Validate() method. The validation result is returned using a custom ValidationResult enum. This way, the reason for an error can be clearly checked.


public enum ValidationResult
{
    Success,
    NameMissing,
    PhoneMissing,
    PhoneInvalid,
    AddressMissing
}

public class Company
{
    public string Name { get; set; } = string.Empty;
    public string Phone { get; set; } = string.Empty;
    public string Address { get; set; } = string.Empty;
    public string Sector { get; set; } = string.Empty;

    public void PrintInfo()
    {
        Console.WriteLine("=== Company Information ===");
        Console.WriteLine($"Name    : {Name}");
        Console.WriteLine($"Phone   : {Phone}");
        Console.WriteLine($"Address : {Address}");
        Console.WriteLine($"Sector  : {Sector}");
    }

    public ValidationResult Validate()
    {
        if (string.IsNullOrWhiteSpace(Name))
            return ValidationResult.NameMissing;

        if (string.IsNullOrWhiteSpace(Phone))
            return ValidationResult.PhoneMissing;

        if (!Phone.All(char.IsDigit))
            return ValidationResult.PhoneInvalid;

        if (string.IsNullOrWhiteSpace(Address))
            return ValidationResult.AddressMissing;

        return ValidationResult.Success;
    }
}

class Program
{
    static void Main()
    {
        var company = new Company();

        Console.Write("Enter company name: ");
        company.Name = Console.ReadLine();

        Console.Write("Enter phone: ");
        company.Phone = Console.ReadLine();

        Console.Write("Enter address: ");
        company.Address = Console.ReadLine();

        Console.Write("Enter sector: ");
        company.Sector = Console.ReadLine();

        var result = company.Validate();

        if (result == ValidationResult.Success)
        {
            Console.WriteLine();
            company.PrintInfo();
        }
        else
        {
            Console.WriteLine($"Error: {result}");
        }
    }
}

In this example, the Validate() method checks whether the entered data is valid and returns an enum. Instead of simply returning true/false, it specifies the exact reason for the failure. For instance, if the company name is missing, the phone number is empty, or the phone contains non-digit characters, different values are returned. This approach makes error handling more readable and easier to maintain.