C#: Expression-bodied members
New C# introduces expression-bodied members of classes. Instead of property or method that makes some simple calculation we can define expressions and keep our code shorter. Nice thing is – also other .NET languages are able to use these new members. Let’s see what we can do with expression-bodied members in C#.
What is expression-bodied member?
Expression-bodied member is member of class that is defined as expression. Something like this:
public double X { get; set; }
public double Y { get; set; }
public double Dist => Math.Sqrt(X * X + Y * Y);
Expression-bodied members can also use arguments.
Why expression-bodied members?
I see expresion-bodied members as a good solution for classes with many small calculated properties. Be it invoice with invoice lines or project with tasks hierarchy – there are always some calculations: line total, total taxes, time spent on task etc.
By example, this code for invoice calculations:
public class InvoiceLine
{
public decimal Amount { get; set; }
public decimal UnitPrice { get; set; }
public decimal DiscountRate { get; set; }
public decimal VatRate { get; set; }
// …
public decimal NetTotal
{
get
{
return Amount * UnitPrice * (1 – DiscountRate);
}
}
public decimal Total
{
get
{
return NetTotal * (1 + VatRate);
}
}
public decimal Discount
{
get
{
return Amount * UnitPrice * DiscountRate;
}
}
}
can be written like this now:
public class InvoiceLine
{
public decimal Amount { get; set; }
public decimal UnitPrice { get; set; }
public decimal DiscountRate { get; set; }
public decimal VatRate { get; set; }
// …
public decimal NetTotal => Amount * UnitPrice * (1 – DiscountRate);
public decimal Total => NetTotal * (1 + VatRate);
public decimal Discount => Amount * UnitPrice * DiscountRate;
}
It’s way shorter and also easier to read and in case of these members we can use them as properties:
foreach(var line in invoice.Lines)
{
invoiceTotal += line.Total;
}
Not much changed in our code and we were able to make our class way shorter with no breaking changes to functionality.
Using expressions with arguments
Expressions can also use arguments. Suppose we have classes for tasks and worklogs.
public class Worklog
{
public Guid Id { get; set; }
public DateTime Date { get; set; }
public long TimeSpent { get; set; }
public User User { get; set; }
}
public class Task
{
public Guid Id { get; set; }
public string Title { get; set; }
public IList<Worklog> Worklogs { get; set; }
// …
public long TimeSpentThru(DateTime begin, DateTime end) =>
Worklogs.Where(w => w.Date >= begin.Date &&
w.Date <= end.AddDays(1).Date)
.Sum(w => w.TimeSpent);
}
TimeSpentThru is expression-bodied member that takes two arguments: beginning and and of time range for what time is calculated. In code we call this expression similar to all other methods.
foreach(var task in project.Tasks)
{
hoursPerMonth += task.TimeSpentThru(monthStarts, monthEnds);
}
Can other languages understand these members?
Now you probably want to ask if other languages are able to understand expression-bodied members. Yes, they are because for them there is nothing new. Let’s take a look at Task class after compilation. We are using dotPeek to see the source of compiled class.
You can see that expression-bodied member TimeSpent (member with no arguments) is compiled as property and TimeSpentThru – the one with arguments – is compiled as method.
Wrapping up
Support for expression-bodied members is great new feature in C# because it helps us to make our classes smaller – specially the ones that have many small calculations through properties and methods. The reason why we can use these members as any other property or method is simple – after compilation they are actually properties and methods which means that also other .NET languages will understand our expression-bodied members.
Looks similar to coffee script with “EXPRESSIONS”