Advertisement
Intermediate C# Lesson 2 of 10

Lesson 2: Fields, Properties & Methods in C#

In Lesson 1, we learned that classes bundle state (data) and behavior (methods) together. Now we'll dive deeper into the building blocks: fields hold data, properties control access to that data, and methods define what a class can do. This lesson covers the differences between them and when to use each one.

Advertisement

Fields vs Properties

A field is a variable inside a class. A property wraps a field with getter and setter logic, giving you control over how data is accessed. In modern C#, properties are preferred because they provide encapsulation without the boilerplate.

Old Way: Bare Fields

public class BankAccount
{
    // Public field — anyone can read or modify directly
    public string AccountHolder;
    public decimal Balance;
}

The problem: no validation. You can set balance to a negative number, which is nonsense for a bank account.

Better Way: Properties with Getters & Setters

public class BankAccount
{
    private decimal _balance;  // Private field (underscore by convention)

    // Property with validation
    public decimal Balance
    {
        get { return _balance; }
        set
        {
            if (value >= 0)
                _balance = value;
            else
                Console.WriteLine("Error: balance cannot be negative");
        }
    }
}

Now setting a negative balance is rejected. The field is hidden (private), and the property controls access.

Auto-Properties (Modern Approach)

When you don't need complex logic, C# offers auto-properties — a shorthand that reduces boilerplate:

public class Person
{
    // Auto-property: compiler creates a hidden backing field
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

// Usage
var person = new Person { FirstName = "John", LastName = "Doe", Age = 30 };

The compiler generates the backing field automatically. This is clean and readable.

Read-Only Properties

Use an init accessor if a property should be set only during construction:

public class Employee
{
    public int EmployeeId { get; init; }  // Can only set during construction
    public string Name { get; set; }      // Can be changed anytime
}

// Usage
var emp = new Employee { EmployeeId = 101, Name = "Alice" };
emp.Name = "Alice Smith";         // ✓ OK
emp.EmployeeId = 999;             // ✗ Compile error!

💡 Rule of Thumb: Use auto-properties by default. Reach for explicit getters/setters only when you need validation or side effects (e.g., logging).

Methods: Defining Class Behavior

A method is a function that belongs to a class. Methods define what actions objects of that class can perform. The syntax follows this pattern:

public class Calculator
{
    // Method with parameters and return type
    public int Add(int a, int b)
    {
        return a + b;
    }

    // Method with no parameters
    public void PrintHello()
    {
        Console.WriteLine("Hello!");
    }

    // Method with no return type (void)
    public void Greet(string name)
    {
        Console.WriteLine($"Hi, {name}!");
    }
}

// Usage
var calc = new Calculator();
int result = calc.Add(5, 3);       // result = 8
calc.PrintHello();                  // Output: Hello!
calc.Greet("Alice");                // Output: Hi, Alice!

Method Overloading

Methods in the same class can have the same name if they differ in parameter count or parameter types. This is called overloading:

public class Printer
{
    // Overload 1: takes an int
    public void Print(int value)
    {
        Console.WriteLine($"Integer: {value}");
    }

    // Overload 2: takes a string
    public void Print(string text)
    {
        Console.WriteLine($"Text: {text}");
    }

    // Overload 3: takes two strings
    public void Print(string first, string second)
    {
        Console.WriteLine($"{first} {second}");
    }
}

// Usage — the right method is called based on arguments
var printer = new Printer();
printer.Print(42);              // Calls Print(int)
printer.Print("Hello");         // Calls Print(string)
printer.Print("Hello", "World"); // Calls Print(string, string)

Static vs Instance Members

By default, fields and methods belong to objects (instance members). Static members belong to the class itself — all instances share the same static field:

public class Counter
{
    // Static field — shared by all instances
    public static int TotalCount = 0;

    // Instance field — each object has its own
    public int IndividualCount = 0;

    public void Increment()
    {
        TotalCount++;
        IndividualCount++;
    }
}

// Usage
var counter1 = new Counter();
var counter2 = new Counter();

counter1.Increment();  // TotalCount = 1, counter1.IndividualCount = 1
counter2.Increment();  // TotalCount = 2, counter2.IndividualCount = 1

Console.WriteLine(Counter.TotalCount);     // Output: 2 (shared)
Console.WriteLine(counter1.IndividualCount); // Output: 1
Console.WriteLine(counter2.IndividualCount); // Output: 1

Static Methods

Static methods perform work that doesn't depend on instance data. Common use: utility functions.

public class MathUtils
{
    // Static method — can be called without creating an object
    public static int Square(int x)
    {
        return x * x;
    }

    public static double CircleArea(double radius)
    {
        return Math.PI * radius * radius;
    }
}

// Usage
int result = MathUtils.Square(5);           // 25
double area = MathUtils.CircleArea(3.0);   // ≈ 28.27
Advertisement

🧠 Quick Check — Lesson 2

Which of the following is the best practice in modern C# for simple data with no validation logic?

Lesson Summary

Fields are variables; properties wrap fields with getter/setter logic for encapsulation.

Auto-properties reduce boilerplate: public string Name { get; set; }

Use the init accessor to create read-only-after-construction properties.

Methods define behavior. Use them to encapsulate logic and actions that objects can perform.

Method overloading lets you reuse the same method name with different parameters.

Static members belong to the class; instance members belong to objects. Use static for shared or utility code.

Up Next

Lesson 3: Constructors & Object Creation

Next Lesson →