Back to Blog
C#

C# Expression-Bodied Property: Syntax and Usage

C# expression-bodied properties let you define a getter as one expression. See the syntax, examples, common pitfalls, and when a traditional getter is a better choice.

C#Expression-bodied membersProperty syntaxCode readabilityC# language features
Illustration of a C# expression-bodied property showing concise arrow syntax for a getter.

A C# expression-bodied property lets you write a property getter as a single expression using the => arrow. Instead of a full get block, you provide the value directly after the arrow. This syntax is part of the expression-bodied members feature introduced in C# 6 for methods and properties, and later extended to other member kinds.

The Basic Syntax

The most common use is a read-only computed property. Consider a Person class with first and last name fields:

public class Person { public string FirstName { get; set; } public string LastName { get; set; } // Traditional getter public string FullNameTraditional { get { return $"{FirstName} {LastName}"; } } // Expression-bodied property public string FullName => $"{FirstName} {LastName}"; }

Both properties are read-only. The expression-bodied version is shorter and often clearer when the logic is a single expression. The arrow syntax is not a lambda; it is a compiler shorthand that generates the same IL as the traditional getter.

When an Expression-Bodied Property Makes Sense

Use this syntax when the getter is a simple mapping, a derived value, or a direct return of a field. Typical examples include:

  • Formatting strings from other properties.
  • Returning a constant or a default value.
  • Delegating to a method or another property.
  • Performing a simple calculation like Length * Width.

The benefit is readability: the property's purpose is visible in one line. For more complex logic that requires multiple statements, a full get block is clearer and easier to debug.

Common Mistakes and Misunderstandings

A few pitfalls appear regularly when developers first use expression-bodied properties.

The single-expression form only defines a getter. The => property syntax shown here is read-only. If you need a setter, use a normal property with separate get and set accessors. C# 7 also lets those accessors be expression-bodied, as in public string Name { get => _name; set => _name = value; }, but that is a different form from the single-expression property in this article.

It is not a lambda expression. Although the arrow looks like a lambda, the compiler treats it as an expression body. There is no delegate creation, no closure, and no additional allocation beyond what the expression itself causes.

Statements are not allowed. The body must be an expression, not a statement. You cannot use if, return, or var inside it. If you need those, use a full getter.

Evaluation happens on every access. Each time you read the property, the expression is evaluated. If the expression performs a costly calculation or allocates a new object, that work repeats. This is the same behavior as a traditional getter, but the concise syntax can make the cost less obvious.

Performance and Allocation Considerations

Because the expression is evaluated on every access, the cost depends entirely on what the expression does. A simple field return like public int Count => _count; compiles to the same IL as a traditional getter and has no overhead. However, an expression that creates a new object, such as public List<int> Items => _items.ToList();, allocates a new list each time it is read.

If the getter is called frequently and the calculation is expensive, consider caching the result. For example, you can use a lazy-initialized backing field:

private List<int> _itemsCache; public List<int> Items => _itemsCache ??= _items.ToList();

This pattern delays the conversion until the first access and reuses the result afterward. It adds a few lines but can be worthwhile when the property is read often. Without measurement, avoid assuming a performance problem; the expression-bodied syntax itself adds no overhead.

Expression-Bodied Members Beyond Properties

The same arrow syntax can be used for methods, constructors, finalizers, indexers, and operators. This consistency can make a class more uniform. For example:

public class Calculator { public int Add(int a, int b) => a + b; public Calculator() => Console.WriteLine("Created"); ~Calculator() => Console.WriteLine("Finalized"); public int this[int index] => _values[index]; public static Calculator operator +(Calculator a, Calculator b) => new Calculator(); }

Methods with expression bodies are useful for one-liners. Constructors and finalizers are less common but can keep the code compact when the body is trivial. Indexers benefit when they simply delegate to a collection.

Compatibility and Language Version Requirements

Expression-bodied properties and methods require C# 6 or later. Later C# releases expanded expression-bodied syntax to constructors, finalizers, accessors, and indexers. If you are targeting an older compiler, verify the language version your project uses before relying on these features. In a .csproj file, you can control this with <LangVersion>. For example, expression-bodied accessors require C# 7, and the ??= pattern in the caching example requires C# 8. If you do not set <LangVersion>, the compiler selects a language version based on the project's target framework, so most modern .NET projects use a current language version by default.

Maintainability: When to Avoid

An expression-bodied property is concise, but that conciseness can become a liability when the getter grows. If you find yourself adding multiple conditions or needing to log, a full getter is easier to read and modify. For example:

// Hard to read as an expression public string Status => _state == State.Active ? "Active" : _state == State.Pending ? "Pending" : "Unknown"; // Better as a full getter public string Status { get { switch (_state) { case State.Active: return "Active"; case State.Pending: return "Pending"; default: return "Unknown"; } } }

Another maintainability concern is debugging. You can set a breakpoint on the line containing the expression-bodied property, but you cannot step into the expression itself. If you need to inspect intermediate values, a full getter with local variables is more convenient.

Finally, consider team familiarity. While the syntax is standard, some developers may not have seen it. For a small team, consistency matters more than brevity. If the team prefers traditional getters, using expression bodies only for trivial properties is a reasonable compromise.

C# Expression-Bodied Property: Syntax, Pitfalls, and Best Practices | RYUSLOG DEV