Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 4

C# Properties and Object Initializers

Recommended resources

Welcome to our next lesson. In our previous session, we delved into the fundamental difference between value types and reference types in C#, establishing how they are stored and behave in memory. You learned that classes serve as blueprints for objects whose data lives on the heap, and these objects contain fields to hold their state.

Today, we will build directly on that foundation. We'll explore C# properties, a core language feature that provides a powerful and elegant way to control access to the data within your objects. For someone with your background in Java, this will feel like a significant upgrade over the traditional getX() and setX() methods. We'll cover the full syntax, the much more common auto-property shorthand, and a clean syntax for object creation called object initializers.

By the end of this lesson, you will be able to effectively use C# properties, auto-properties, and object initializers to create well-encapsulated and easy-to-use classes.

1. Encapsulation: From Java Getters/Setters to C# Properties

In object-oriented programming, a key principle is encapsulation: bundling data (fields) and the methods that operate on that data within a single unit (a class), and restricting direct access to the data from outside.

In Java, you achieve this by declaring fields as private and exposing them through public get and set methods.

// The Java way
public class Product {
    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

This pattern works, but it can be verbose. C# provides a more integrated and cleaner solution called properties. A property looks like a public field to the code that uses it, but internally it's implemented with special methods called accessors.

Let's start by looking at a property with a full implementation, which is the direct equivalent of the Java example above.

// The C# way with a full property implementation
public class Product
{
    private string _name; // This is the "backing field"

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

Here's what's happening:

  • _name is a private field that actually stores the data. This is called the backing field. The underscore prefix is a common convention for private backing fields.
  • Name is the public property.
  • The get accessor is a method that runs when you read the property's value. It returns the value of the backing field.
  • The set accessor is a method that runs when you assign a value to the property. The new value is implicitly available through the value keyword.

From the outside, you use it just like a field, which is much cleaner:
myProduct.Name = "Laptop"; (This calls the set accessor)
Console.WriteLine(myProduct.Name); (This calls the get accessor)

The following video provides an excellent introduction to properties, explaining why they are preferable to public fields and demonstrating the full syntax with backing fields.

#4 | PROPERTIES & GET/SET METHODS 🎮 | Unity For Beginners | Unity Tutorial

Watch this video from Dani Krossing, which introduces the concept of properties.

Focus on the initial explanation of why properties are used instead of public fields. Then, pay close attention to the demonstration of the full property syntax, where he creates a public property with get and set accessors that wrap a private backing field.


2. The Power of Properties: Validation and Computed Values

The real power of properties comes from the ability to add logic inside the get and set accessors. Since they are methods, you can do anything you want in them. This is where they truly shine over simple public fields.

  • Validation in the set accessor: You can ensure that an object is always in a valid state.
  • Computation in the get accessor: You can return a value that is derived from other data.

Let's enhance our Product class with some logic:

public class Product
{
    private decimal _price;
    public string Name { get; set; } // We'll get to this shorthand next!
    public decimal Tax { get; set; }

    public decimal Price
    {
        get { return _price; }
        set
        {
            if (value < 0)
            {
                throw new ArgumentException("Price cannot be negative.");
            }
            _price = value;
        }
    }

    // A "computed" or "read-only" property
    public decimal TotalPrice
    {
        get { return _price + (_price * Tax); }
    }
}

Notice a few things here:

  1. The Price property's setter now includes validation logic.
  2. The TotalPrice property is read-only because it only has a get accessor. Its value is calculated on the fly each time it's accessed. It doesn't need a backing field because it doesn't store any state itself.

The video you just watched also demonstrates how to add this kind of custom logic.

#4 | PROPERTIES & GET/SET METHODS 🎮 | Unity For Beginners | Unity Tutorial

Now, continue with the same video from Dani Krossing.

Watch the section where he explains the benefits of this approach by adding clamping logic to the set method to control the allowed values. He also demonstrates creating a second, read-only property to display the data in a different format, illustrating a computed property.


3. Auto-Implemented Properties

In many cases, you don't need any special logic in your accessors. You just want a simple property that gets and sets a value. For this common scenario, C# provides a beautifully concise syntax called auto-implemented properties or auto-properties.

// The concise C# way using an auto-property
public class Student
{
    public string Name { get; set; }
    public string Department { get; set; }
}

This single line of code, public string Name { get; set; }, does everything the multi-line Product example did earlier. When you use this syntax, the C# compiler automatically generates a hidden, private backing field for you and wires up the get and set accessors to it.

This is the most common way you will see properties declared in modern C# code. It gives you the encapsulation of properties with the brevity of a public field. And if you later need to add validation logic, you can easily expand it to a full property with a backing field without changing any of the code that uses the class.

The image below gives a nice overview of a C# class, highlighting the difference between fields (which store data directly) and properties (which provide controlled access via get/set).

This annotated C# code shows the different members of a class. Note the 'Fields' at the top, which are direct data storage, and the 'Properties' at the bottom, which use the `{ get; set; }` auto-property syntax for controlled access.

For a more in-depth look, the official Microsoft documentation is the best resource.

Properties (C# Programming Guide)

This article from the official Microsoft C# guide explains properties from the ground up.

First, read the introduction which defines a property as a member that provides a flexible mechanism. Then, read the section on "Automatically implemented properties" which introduces the concise syntax and explains that the compiler generates the backing field for you.


4. Immutability and Object Initializers

Your Java experience has likely taught you the value of immutable objects. You can achieve this in C# by creating read-only properties. In modern C#, we have a special accessor called init.

An init-only property can only be assigned a value during object initialization. After the object is created, it becomes read-only.

public class Transaction
{
    public Guid TransactionId { get; init; } // Can only be set when the object is created
    public decimal Amount { get; set; }
    public DateTime Timestamp { get; } // Can only be set in the constructor

    public Transaction()
    {
        Timestamp = DateTime.UtcNow;
    }
}

This brings us to another convenient feature: object initializers. This syntax allows you to create an object and set its public properties in a single, readable statement, without needing to define multiple constructors.

// Using an object initializer
var transaction = new Transaction
{
    TransactionId = Guid.NewGuid(),
    Amount = 19.99m // 'm' suffix denotes a decimal literal
};

// This is now illegal because TransactionId is init-only:
// transaction.TransactionId = Guid.NewGuid(); // Compile-time error!

This is incredibly useful and avoids the boilerplate of creating many constructor overloads, a pattern you might have used or seen in Java.

The following article does a great job of explaining properties and object initializers from the perspective of a Java developer.

Learning C# After Java: 5 Differences Explored - DEV Community

This article from DEV Community is written specifically for developers moving from Java to C#.

Focus on the section titled Get/Set Properties. It draws a direct parallel to Java getters/setters, shows auto-properties with get, set, and init, and demonstrates how to use an object initializer.

To see a very clear visual explanation of why object initializers are so useful, this short video from Programming with Mosh is perfect.

C# Object Initializers Tutorial

This video focuses entirely on object initializers.

Watch the entire video. Mosh first explains why we need them (to avoid creating multiple constructors) and then demonstrates the clean object initialization syntax.


Conclusion

In this lesson, we've replaced the verbose getter/setter pattern from Java with the more elegant and powerful C# properties. This is a fundamental shift that leads to cleaner, more readable, and more maintainable code.

Let's summarize the key points:

  • Properties provide controlled access to an object's data, replacing the need for explicit getX() and setX() methods. They consist of get and set accessors.
  • Full Properties use an explicit private backing field and are used when you need custom logic for validation or computation.
  • Auto-Properties ({ get; set; }) are a concise syntax for simple properties where the compiler generates the backing field automatically. This is the most common form.
  • init Accessors are used to create immutable properties that can be set only during object creation.
  • Object Initializers provide a flexible syntax to create and assign properties to an object in one step, reducing the need for multiple constructor overloads.

You now have a solid understanding of how to define the data and behavior of your C# classes. In our next lesson, we will tackle another crucial topic: how C# helps you safely handle the absence of a value. We'll explore nullable types and null-coalescing operators, features that significantly improve how you work with null.