C#
properties
methods
programming
object-oriented programming

Are C properties actually Methods?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Yes, C# properties are methods under the hood. The compiler transforms each property into one or two methods — get_PropertyName() and set_PropertyName(value) — in the generated IL (Intermediate Language). Properties are syntactic sugar that provides field-like access syntax while executing method logic. This means properties can have side effects, throw exceptions, be virtual, and participate in interfaces, just like regular methods.

Properties Compile to Methods

csharp
1public class Person
2{
3    private string _name;
4
5    public string Name
6    {
7        get { return _name; }
8        set { _name = value; }
9    }
10}

The C# compiler generates IL equivalent to:

csharp
1// What the compiler actually generates (pseudo-code)
2public class Person
3{
4    private string _name;
5
6    public string get_Name()
7    {
8        return _name;
9    }
10
11    public void set_Name(string value)
12    {
13        _name = value;
14    }
15}

You can verify this with reflection or by examining the IL in a decompiler like ILSpy or dnSpy:

csharp
1var methods = typeof(Person).GetMethods();
2foreach (var m in methods)
3    Console.WriteLine(m.Name);
4
5// Output includes:
6// get_Name
7// set_Name
8// GetType
9// ToString
10// Equals
11// GetHashCode

Auto-Properties Also Generate Methods

csharp
1public class Product
2{
3    // Auto-property — compiler generates a hidden backing field
4    public decimal Price { get; set; }
5}

This compiles to:

csharp
1public class Product
2{
3    private decimal <Price>k__BackingField;  // Compiler-generated field
4
5    public decimal get_Price()
6    {
7        return <Price>k__BackingField;
8    }
9
10    public void set_Price(decimal value)
11    {
12        <Price>k__BackingField = value;
13    }
14}

The backing field has a name that is invalid in C# (<Price>k__BackingField) so you cannot accidentally reference it in your code.

Properties vs Fields in IL

csharp
1public class Example
2{
3    public int Field;           // Actual field — no method call
4    public int Property { get; set; }  // Property — method calls
5}
6
7// Usage looks identical:
8var e = new Example();
9e.Field = 42;       // IL: stfld (direct field access)
10e.Property = 42;    // IL: call set_Property (method call)
11
12int a = e.Field;    // IL: ldfld (direct field access)
13int b = e.Property; // IL: call get_Property (method call)

Field access is a single IL instruction (ldfld/stfld). Property access is a method call (call/callvirt). However, the JIT compiler typically inlines simple property getters and setters, making them as fast as direct field access.

Properties Can Do Things Fields Cannot

Because properties are methods, they support features that fields do not:

csharp
1public class Temperature
2{
3    private double _celsius;
4
5    // Computed value — no backing field
6    public double Fahrenheit => _celsius * 9.0 / 5.0 + 32;
7
8    // Validation in the setter
9    public double Celsius
10    {
11        get => _celsius;
12        set
13        {
14            if (value < -273.15)
15                throw new ArgumentException("Below absolute zero");
16            _celsius = value;
17        }
18    }
19
20    // Different access levels
21    public string Id { get; private set; }
22
23    // Virtual — can be overridden
24    public virtual string Display => $"{_celsius}°C";
25}

None of these are possible with fields:

  • Fields cannot compute values
  • Fields cannot validate on assignment
  • Fields cannot have different get/set accessibility
  • Fields cannot be virtual or abstract

Interface Properties Are Method Contracts

csharp
1public interface IIdentifiable
2{
3    string Id { get; }  // Requires a get_Id() method
4}
5
6public class User : IIdentifiable
7{
8    public string Id { get; set; }  // Satisfies the interface
9}
10
11// In IL, the interface defines:
12// .method public abstract virtual string get_Id()

When an interface declares a property, it is declaring an abstract method. The implementing class provides the method body through its property accessor.

Reflection Treats Properties and Methods Differently

csharp
1var type = typeof(Person);
2
3// PropertyInfo — the "property" abstraction
4var prop = type.GetProperty("Name");
5Console.WriteLine(prop.GetValue(person));     // Calls get_Name()
6prop.SetValue(person, "Alice");               // Calls set_Name("Alice")
7
8// You can also get the underlying methods
9MethodInfo getter = prop.GetGetMethod();
10MethodInfo setter = prop.GetSetMethod();
11
12Console.WriteLine(getter.Name);  // "get_Name"
13Console.WriteLine(setter.Name);  // "set_Name"
14
15// Invoke the getter method directly
16string name = (string)getter.Invoke(person, null);

The .NET type system maintains both the property metadata and the underlying method metadata, so you can interact with either abstraction.

Performance: Properties vs Fields

csharp
1// Simple auto-property — JIT inlines to direct field access
2public int Count { get; set; }
3
4// Complex property — not inlined, has method call overhead
5public int Count
6{
7    get
8    {
9        lock (_lock)
10        {
11            return _count;
12        }
13    }
14}

For simple getters and setters, the JIT compiler inlines the method call, producing the same machine code as direct field access. Complex properties with locking, validation, or logging are not inlined and have real method call overhead.

Common Pitfalls

  • Assuming properties are free like fields: Properties are method calls. A property getter that performs database queries, file I/O, or expensive calculations runs that code every time it is accessed. Design guidelines recommend that property getters should be fast and side-effect-free.
  • Naming conflicts with generated methods: Since properties generate get_X() and set_X() methods, you cannot define a method named get_Name() if you also have a property named Name. The compiler rejects this as a duplicate method definition.
  • Debugging auto-property backing fields: Auto-property backing fields (<Name>k__BackingField) appear in debuggers and serializers. Some serializers (like BinaryFormatter) serialize the backing field name, which can break if you rename the property.
  • Properties in structs and boxing: Accessing a property on a boxed struct creates a copy. Mutating a property on a struct through an interface reference modifies the copy, not the original, because structs are value types.
  • Using properties in tight loops: While the JIT inlines simple properties, virtual properties and properties behind interfaces require indirect calls (callvirt) that cannot be inlined. In performance-critical loops, this overhead can be measurable.

Summary

  • C# properties compile to get_ and set_ methods in IL
  • Auto-properties generate a hidden backing field and accessor methods
  • Properties support validation, computation, access modifiers, virtual dispatch, and interfaces — fields do not
  • The JIT compiler inlines simple property accessors to match field access performance
  • Reflection exposes both the PropertyInfo and the underlying MethodInfo for each accessor
  • Design guidelines recommend keeping property getters fast and free of side effects

Course illustration
Course illustration

All Rights Reserved.