Expression-bodied members arrived in C# 6.0 for methods and read-only properties, expanded in C# 7.0 to include constructors, destructors, accessors, and indexers, and further extended in later versions. They let you replace a multi-line statement body with a single expression using =>, making simple getters, validators, and computed properties more concise and declarative.
Why it matters
Expression-bodied members make code cleaner and closer to functional style. For properties that compute a value, methods that perform a single operation, or indexers that transform input, arrow syntax eliminates visual noise. Combined with records and immutable patterns, they are foundational to modern C# style.
Practical examples
Single computed property:
public decimal DiscountedPrice => Price * (1 - Discount);
Simple validation method:
public bool IsOverdue => DateTime.Now > DueDate;
Chained property access:
public string FullName => $"{FirstName} {LastName}";
Operator overload:
public static Vector operator +(Vector a, Vector b) => new(a.X + b.X, a.Y + b.Y);
Getter and setter with computed backing:
public int Age => DateTime.Now.Year - BirthYear;
Cautions
Expression-bodied members work best for simple logic. Complex multi-step operations, loops, or error handling belong in statement-body methods. Avoid expressions that are hard to read in one line. They also cannot contain statements like throw, assignment, or loop constructs (though some restrictions relaxed in C# 7.0+).
Expression-bodied methods and properties
Replace statement bodies with a single expression using the => syntax.
Valid since C# 6.0
// Before: Statement bodies with braces
public class PersonOld
{
private string firstName;
private string lastName;
public string GetFullName()
{
return firstName + " " + lastName;
}
public bool IsAdult
{
get
{
return Age >= 18;
}
}
public int Age
{
get
{
return DateTime.Now.Year - BirthYear;
}
}
public int BirthYear { get; set; }
}
// After: Expression-bodied members
public class PersonNew
{
private string firstName;
private string lastName;
public string GetFullName() => firstName + " " + lastName;
public bool IsAdult => Age >= 18;
public int Age => DateTime.Now.Year - BirthYear;
public int BirthYear { get; set; }
}
Expression-bodied accessors and indexers
Use arrow syntax with getters, setters, and indexers.
Valid since C# 6.0
public class AccessorsAndIndexers
{
private string[] _items = new[] { "apple", "banana", "cherry" };
private int _accessCount;
// Expression-bodied auto-property getter
public int ItemCount => _items.Length;
// Expression-bodied custom getter
public int AccessCount => _accessCount;
// Expression-bodied setter (C# 6.0+)
public int Value { get; set; }
// Indexer with expression body
public string this[int index]
{
get => _items[index];
set => _items[index] = value;
}
// Indexer with computed result
public char this[int index, int charIndex]
=> _items[index][charIndex];
// Method with side effect (incrementing access count)
public string GetItem(int index)
{
_accessCount++;
return _items[index];
}
// Read-only property with LINQ
public IEnumerable<string> SortedItems => _items.OrderBy(x => x);
// Ternary expression in property
public string FirstOrDefault => _items.Length > 0 ? _items[0] : "empty";
}
Expression-bodied constructors and operators
Define constructors and operators concisely with expressions.
Valid since C# 7.0
public class Point
{
public double X { get; set; }
public double Y { get; set; }
// Expression-bodied constructor (C# 7.0+)
public Point(double x, double y) => (X, Y) = (x, y);
// Expression-bodied method
public double Distance() => Math.Sqrt(X * X + Y * Y);
// Expression-bodied operator overload
public static Point operator +(Point a, Point b)
=> new Point(a.X + b.X, a.Y + b.Y);
public static Point operator -(Point a, Point b)
=> new Point(a.X - b.X, a.Y - b.Y);
public static Point operator *(Point p, double scalar)
=> new Point(p.X * scalar, p.Y * scalar);
// Expression-bodied method for validation
public bool IsOrigin => X == 0 && Y == 0;
// Expression-bodied method returning tuple
public (double x, double y) AsAngle() => (
X / Distance(),
Y / Distance()
);
// Explicit conversion using expression body
public static explicit operator Point(string coords)
{
var parts = coords.Split(',');
return new Point(double.Parse(parts[0]), double.Parse(parts[1]));
}
// ToString as expression body
public override string ToString() => $"({X}, {Y})";
}
// Record type (C# 9.0+) uses expression bodies by default
public record Rectangle(double Width, double Height)
{
public double Area => Width * Height;
public double Perimeter => 2 * (Width + Height);
public bool IsSquare => Width == Height;
}