Expression-bodied members are members whose bodies are an expression rather than a statement block. In many common situations, they make code more readable and compact.
The following listing shows a traditional method declaration:
public override string ToString()
{
return "A textual representation of the object..";
}
Using an expression-bodied member, the same method declaration is significantly more compact:
public override string ToString() => "A textual representation of the object..";
Internally, the compiler resolves expression-bodied members by creating a statement block. Expression-bodied members are syntatic sugar and that is all -- these two code samples are functionality identical. The difference is that, the expression-bodied member is more compact.
As you can now see, expression-bodied members enable a more compact syntax by removing the need to create a statement block and explicitly return a value.
In the above example, the string literal expression implicitly returns a string
. You do not have to return a value from an expression-bodied member, though. For void
-returning methods - and Task
-returning async
methods - the expression must be a statement expression:
public void NewHello() => Console.WriteLine("Hello, World!"); // Expression-bodied member
public void OldHello() // Traditional statement block
{
Console.WriteLine("Hello, World!");
}
Inside of expression-bodied members, the same rules that govern return
statements in traditional methods apply: you must return a value from an expression-bodied member with a non-void
return type and you cannot return a value from an expression-bodied member with a void
return type.
Up until now we have only looked at expression-bodied methods. Expression-bodied members also work on automatic properties (including indexers):
public double NewArea => Math.Pow(Radius, 2) * Math.PI; // Expression-bodied member.
public double OldArea // Traditional statement block
{
get
{
return Math.Pow(Radius, 2) * Math.PI;
}
}
As you can see, expression-bodied members have a handful of shortcuts.
- There is no need to explicitly
return
a value because the compiler can infer that you want to return the result of the expression. - There is also no need to use the
get
keyword because it is implied by the use of the expression-bodied member syntax (the=>
). - There is no need to create a statement block becuse we only have a single expression
When working with properties make sure you watch out for this Watch out for this gotcha documented by Bill Wagner, though!
If you are not still convinced about the benefits of expression-bodied members, then please, let me try and convince you of them.
Say we want to create the most quintessential example of object-oriented design principles: the Circle
class. In C# 5 we would do this:
public class Circle
{
public int Radius { get; private set; }
public Point Point { get; private set; }
public double Area
{
get
{
return Math.Pow(Radius, 2) * Math.PI;
}
}
public double Perimiter
{
get
{
return 2 * Math.PI * Radius;
}
}
public Circle(Point point, int radius)
{
Point = point;
Radius = radius;
}
public override string ToString()
{
return "A textual representation of the object..";
}
}
Using expression-bodied members, we can express the same Circle
class in a more compact and readable manner:
public class Circle
{
public int Radius { get; private set; }
public Point Point { get; private set; }
public double Area => Math.Pow(Radius, 2) * Math.PI;
public double Perimiter => 2 * Math.PI * Radius;
public Circle(Point point, int radius)
{
Point = point;
Radius = radius;
}
public override string ToString() => "A textual representation of the object..";
}
Here I have removed almost 15 lines of code - code which didn't actually do anything - in a relatively small code sample. You can imagine how many more lines you could save in an actual code base over time.
No, not necessarily. Short code is not necessarily synonymous with readable code and readable code is what we should strive for. I do think that, in the above example, we have improved readability because now it is possible to focus on the code and not the syntax that surrounds it (the brace, the statements etc.). What do you think? Let me know in the comments below.
Now that we have a sound understanding (and hopefuly, appreciation) of expression-bodied members, let us examine the syntax.
The =>
part is pronounced "arrow" and tells the compiler you are using an expression-bodied member. The code that follows the arrow (=>
) must be a single expression. It cannot be a statement block with control flow like this:
public string Name => {
bool nightTime = true;
if (nightTime)
{
return "Batman";
}
return "Bruce Wayne";
}
This code results in a firm compilation error. If you find yourself wanting to do something like this then use a nominal declaration instead.
Some people find that it helsp to think of the =>
part as "goes to", so the example above could be read "ToString
goes to Radius
".
If you are familiar with lambda expressions - introduced in C# 3.0 - you may be tempted to think of expression-bodied members as lambda expressions. Expression-bodied members are lambda expression-like but they are not lambda expressions. Lambda expressions result in either a delegate instance or an expression tree as where expression-bodied members do not. What's more, lambda expressions actually have quite different syntax rules compared to expression-bodied members. For example, a lambda expression's code can be a statement block instead of an expression. Like we just saw, an expression-bodied member's code absoloutely has to be a single expression.
It is a well known fact that we, as coders, spend a lot more time reading code than we do actually writing code, and if we spend so much time reading code, that does not leave us a lot of time to actually write code. It is for this reason that features like this one, which make our code more concise and easier to read are a very welcome addition to the langauge. You do need to be judicious about how you use them, though. If you use them too heavily you may end up hurting readability which defeats their purpose.
Expression-bodied members will not work for constructors or fields however.