Chapter 8. Super-sleek properties and expression-bodied members

This chapter covers

  • Implementing read-only properties automatically
  • Initializing automatically implemented properties at their declaration
  • Removing unnecessary ceremony with expression-bodied members

Some versions of C# have one big, unifying feature that almost all other features contribute to. For example, C# 3 introduced LINQ, and C# 5 introduced asynchrony. C# 6 isn’t like that, but it does have a general theme. Almost all the features contribute to cleaner, simpler, and more readable code. C# 6 isn’t about doing more; it’s about doing the same work with less code.

The features you’ll look at in this chapter are about properties and other simple pieces of code. When not much logic is involved, removing even the smallest piece of ceremony—braces and return statements, for example—can make a big difference. Although the features here may not sound impressive, I’ve been surprised at their impact in real code. We’ll start off looking at properties and move on to methods, indexers, and operators.

8.1. A brief history of properties

C# has had properties from the first version. Although their core functionality hasn’t changed over time, they’ve gradually become simpler to express in source code and more versatile. Properties allow you to differentiate between how state access and manipulation are exposed in the API and how that state is implemented.

For example, suppose you want to represent a point in 2D space. You could represent that easily using public fields, as shown in the following listing.

Listing 8.1. Point class with public fields
public sealed class Point
{
    public double X;
    public double Y;
}

That doesn’t seem too bad at first glance, but the capabilities of the class (“I can access its X and Y values”) are closely tied to the implementation (“I’ll use two double fields”). But at this point, the implementation has lost control. As long as the class state is exposed directly via fields, you can’t do the following:

  • Perform validation when setting new values (for example, preventing infinite or not-a-number values for the X and Y coordinates)
  • Perform computation when fetching values (for example, if you wanted to store the fields in a different format—unlikely for a point, but perfectly feasible in other cases)

You might argue that you could always change the field to a property later, when you find you need something like this, but that’s a breaking change, which you probably want to avoid. (It breaks source compatibility, binary compatibility, and reflection compatibility. That’s a big risk to take just to avoid using properties from the start.)

In C# 1, the language provided almost no help with properties. A property-based version of listing 8.1 would require manual declaration of the backing fields, along with getters and setters for each of the properties, as shown in the next listing.

Listing 8.2. Point class with properties in C# 1
public sealed class Point
{
    private double x, y;
    public double X { get { return x; } set { x = value; } }
    public double Y { get { return y; } set { y = value; } }
}

You could argue that many properties start off simply reading and writing fields with no extra validation, computation, or anything else and stay that way for the whole history of the code. Properties like that could’ve been exposed as fields, but it’s hard to predict which properties might need extra code later. Even when you can do that accurately, it feels like you’re operating at two levels of abstraction for no reason. To me, properties act as part of the contract that a type provides: its advertised functionality. Fields are simply implementation details; they’re the mechanism inside the box, which users don’t need to know about in the vast majority of cases. I prefer fields to be private in almost all cases.

Note

Like all good rules of thumb, there are exceptions. In some situations, it makes sense to expose fields directly. You’ll see one interesting case in chapter 11 when you look at the tuples provided by C# 7.

The only improvement to properties in C# 2 was to allow different access modifiers for the getter and setter—for example, a public getter and a private setter. (That’s not the only combination available, but it’s by far the most common one.)

C# 3 then added automatically implemented properties, which allow listing 8.2 to be rewritten in a simpler way, as follows.

Listing 8.3. Point class with properties in C# 3
public sealed class Point
{
    public double X { get; set; }
    public double Y { get; set; }
}

This code is almost exactly equivalent to the code in listing 8.2, except there’s no way of accessing the backing fields directly. They’re given unspeakable names, which aren’t valid C# identifiers but are fine as far as the runtime is concerned.

Importantly, C# 3 allowed only read/write properties to be implemented automatically. I’m not going to go into all the benefits (and pitfalls) of immutability here, but there are many reasons you might want your Point class to be immutable. To make your properties truly read-only, you need to go back to writing the code manually.

Listing 8.4. Point class with read-only properties via manual implementation in C# 3
public sealed class Point
{
    private readonly double x, y;             1
    public double X { get { return x; } }     2
    public double Y { get { return y; } }     2

    public Point(double x, double y)
    {
        this.x = x;                           3
        this.y = y;                           3
    }
}

  • 1 Declares read-only fields
  • 2 Declares read-only properties returning the field values
  • 3 Initializes the fields on construction

This is irritating, to say the least. Many developers—including me—sometimes cheated. If we wanted read-only properties, we’d use automatically implemented properties with private setters, as shown in the following listing.

Listing 8.5. Point class with publicly read-only properties via automatic implementation with private setters in C# 3
public sealed class Point
{
    public double X { get; private set; }
    public double Y { get; private set; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
}

That works, but it’s unsatisfying. It doesn’t express what you want. It allows you to change the values of the properties within the class even though you don’t want to; you want a property you can set in the constructor but then never change elsewhere, and you want it to be backed by a field in a trivial way. Up to and including C# 5, the language forced you to choose between simplicity of implementation and clarity of intent, with each choice sacrificing the other. Since C# 6, you no longer need to compromise; you can write brief code that expresses your intent clearly.

8.2. Upgrades to automatically implemented properties

C# 6 introduced two new features to automatically implemented properties. Both are simple to explain and use. In the previous section, I focused on the importance of exposing properties instead of public fields and the difficulties of implementing immutable types concisely. You can probably guess how our first new feature in C# 6 works, but a couple of other restrictions have been lifted, too.

8.2.1. Read-only automatically implemented properties

C# 6 allows genuinely read-only properties backed by read-only fields to be expressed in a simple way. All it takes is an empty getter and no setter, as shown in the next listing.

Listing 8.6. Point class using read-only automatically implemented properties
public sealed class Point
{
    public double X { get; }             1
    public double Y { get; }             1

    public Point(double x, double y)
    {
        X = x;                           2
        Y = y;                           2
    }
}

  • 1 Declares read-only automatically implemented properties
  • 2 Initializes the properties on construction

The only parts that have changed from listing 8.5 are the declarations of the X and Y properties; they no longer have a setter at all. Given that there are no setters, you may be wondering how you’re initializing the properties in the constructor. It happens exactly as it did in listing 8.4, where you implemented it manually: the field declared by the automatically implemented property is read-only, and any assignments to the property are translated by the compiler into direct field assignments. Any attempt to set the property in code other than the constructor results in a compile-time error.

As a fan of immutability, this feels like a real step forward to me. It lets you express your ideal result in a small amount of code. Laziness is now no obstacle to code hygiene, at least in this one small way.

The next limitation removed in C# 6 has to do with initialization. So far, the properties I’ve shown have either not been initialized explicitly at all or have been initialized in a constructor. But what if you want to initialize a property as if it were a field?

8.2.2. Initializing automatically implemented properties

Before C# 6, any initialization of automatically implemented properties had to be in constructors; you couldn’t initialize the properties at the point of declaration. For example, suppose you had a Person class in C# 2, as shown in the following listing.

Listing 8.7. Person class with manual property in C# 2
public class Person
{
    private List<Person> friends = new List<Person>();     1
    public List<Person> Friends                            2
    {
        get { return friends; }
        set { friends = value; }
    }
}

  • 1 Declares and initializes field
  • 2 Exposes a property to read/write the field

If you wanted to change this code to use automatically implemented properties, you’d have to move the initialization into a constructor, where previously you hadn’t explicitly declared any constructors at all. You’d end up with code like the following listing.

Listing 8.8. Person class with automatically implemented property in C# 3
public class Person
{
    public List<Person> Friends { get; set; }      1

    public Person()
    {
        Friends = new List<Person>();              2
    }
}

  • 1 Declares the property; no initializer permitted
  • 2 Initializes the property in a constructor

That’s about as verbose as it was before! In C# 6, this restriction was removed. You can initialize at the point of property declaration, as the following listing shows.

Listing 8.9. Person class with automatically implemented read/write property in C# 6
public class Person
{
    public List<Person> Friends { get; set; } =   1
        new List<Person>();                       1
}

  • 1 Declares and initializes a read/write automatically implemented

Naturally, you can use this feature with read-only automatically implemented properties as well. One common pattern is to have a read-only property exposing a mutable collection, so a caller can add or remove items from the collection but can never change the property to refer to a different collection (or set it to be a null reference). As you might expect, this is just a matter of removing the setter.

Listing 8.10. Person class with automatically implemented read-only property in C# 6
public class Person
{
    public List<Person> Friends { get; } =   1
        new List<Person>();                  1
}

  • 1 Declares and initializes a read-only automatically implemented

I’ve rarely found this particular restriction of earlier versions of C# to be a massive problem, because usually I want to initialize properties based on constructor parameters anyway, but the change is certainly a welcome addition. The next restriction that has been removed ends up being more important in conjunction with read-only automatically implemented properties.

8.2.3. Automatically implemented properties in structs

Before C# 6, I always found automatically implemented properties to be a little problematic in structs. There were two reasons for this:

  • I always write immutable structs, so the lack of read-only automatically implemented properties was always a pain point.
  • I could assign to an automatically implemented property in a constructor only after chaining to another constructor because of the rules about definite assignment.
Note

In general, definite assignment rules are about the compiler keeping track of which variables will have been assigned at a particular point in your code, regardless of how you got there. These rules are mostly relevant for local variables, to make sure you don’t try to read from a local variable that hasn’t been assigned a value yet. Here, we’re looking at a slightly different use of the same rules.

The following listing demonstrates both of these points in a struct version of our previous Point class. Just typing it out makes me squirm a little.

Listing 8.11. Point struct in C# 5 using automatically implemented properties
public struct Point
{
    public double X { get; private set; }           1
    public double Y { get; private set; }           1

    public Point(double x, double y) : this()       2
    {
        X = x;                                      3
        Y = y;                                      3
    }
}

  • 1 Properties with public getters and private setters
  • 2 Chaining to default constructor
  • 3 Property initialization

This isn’t code I would’ve included in a real codebase. The benefits of automatically implemented properties are outweighed by the ugliness. You’re already familiar with the read-only aspect of the properties, but why do you need to call the default constructor in our constructor initializer?

The answer lies in subtleties of the rules around field assignments in structs. Two rules are at work here:

  • You can’t use any properties, methods, indexers, or events in a struct until the compiler considers that all the fields have been definitely assigned.
  • Every struct constructor must assign values to all fields before it returns control to the caller.

In C# 5, without calling the default constructor, you’re violating both rules. Setting the X and Y properties still counts as using the value of the struct, so you’re not allowed to do it. Setting the properties doesn’t count as assigning the fields, so you can’t return from the constructor anyway. Chaining to the default constructor is a workaround because that assigns all fields before your constructor body executes. You can then set the properties and return at the end because the compiler is happy that all your fields were set anyway.

In C# 6, the language and the compiler have a closer understanding of the relationship between automatically implemented properties and the fields they’re backed by:

  • You’re allowed to set an automatically implemented property before all the fields are initialized.
  • Setting an automatically implemented property counts as initializing the field.
  • You’re allowed to read an automatically implemented property before other fields are initialized, so long as you’ve set it beforehand.

Another way of thinking of this is that within the constructor, automatically implemented properties are treated as if they’re fields.

With those new rules in place and genuine read-only automatically implemented properties, the struct version of Point in C# 6 shown in the next listing is identical to the class version in listing 8.6, other than declaring a struct instead of a sealed class.

Listing 8.12. Point struct in C# 6 using automatically implemented properties
public struct Point
{
    public double X { get; }
    public double Y { get; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }
}

The result is clean and concise, just the way you want it.

Note

You may be asking whether Point should be a struct at all. In this case, I’m on the fence. Points do feel like fairly natural value types, but I still usually default to creating classes. Outside Noda Time (which is struct heavy), I rarely write my own structs. This example certainly isn’t trying to suggest you should start using structs more widely, but if you do write your own struct, the language is more helpful than it used to be.

Everything you’ve seen so far has made automatically implemented properties cleaner to work with, which often reduces the amount of boilerplate code. Not all properties are automatically implemented, though. The mission of removing clutter from your code doesn’t stop there.

8.3. Expression-bodied members

Far be it from me to prescribe one specific style of coding in C#. Aside from anything else, different problem domains lend themselves to different approaches. But I’ve certainly come across types that have a lot of simple methods and properties. C# 6 helps you here with expression-bodied members. We’ll start off with properties, since you were looking at them in the previous section, and then see how the same idea can be applied to other function members.

8.3.1. Even simpler read-only computed properties

Some properties are trivial: if the implementation in terms of fields matches the logical state of the type, the property can return the field value directly. That’s what automatically implemented properties are for. Other properties involve computations based on other fields or properties. To demonstrate the problem that C# 6 addresses, the following listing adds another property to our Point class: DistanceFromOrigin, which uses the Pythagorean theorem in a simple way to return how far the point is from the origin.

Note

Don’t worry if the math here isn’t familiar. The details aren’t important, just the fact that it’s a read-only property that uses X and Y.

Listing 8.13. Adding a DistanceFromOrigin property to Point
public sealed class Point
{
    public double X { get; }
    public double Y { get; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double DistanceFromOrigin                   1
    {                                                  1
        get { return Math.Sqrt(X * X + Y * Y); }       1
    }                                                  1
}

  • 1 Read-only property to compute a distance

I’m not going to claim that this is terribly hard to read, but it does contain a lot of syntax that I could describe as ceremony: it’s there only to make the compiler aware of how the meaningful code fits in. Figure 8.1 shows the same property but annotated to highlight the useful parts; the ceremony (braces, a return statement, and a semicolon) are in a lighter shade.

Figure 8.1. Annotated property declaration showing important aspects

C# 6 allows you to express this much more cleanly:

public double DistanceFromOrigin => Math.Sqrt(X * X + Y * Y);

Here, the => is used to indicate an expression-bodied member—in this case, a read-only property. No more braces, no more keywords. Both the read-only property part and the fact that the expression is used to return the value are implicit. Compare this with figure 8.1, and you’ll see that the expression-bodied form has everything that’s useful (with a different way of indicating that it’s a read-only property) and nothing extraneous. Perfect!

No, this isn’t a lambda expression

Yes, you’ve seen this element of syntax before. Lambda expressions were introduced in C# 3 as a brief way of declaring delegates and expression trees. For example:

Func<string, int> stringLength = text => text.Length;

Expression-bodied members use the => syntax but aren’t lambda expressions. The preceding declaration of DistanceFromOrigin doesn’t involve any delegates or expression trees; it only instructs the compiler to create a read-only property that computes the given expression and returns the result.

When talking about the syntax out loud, I usually describe => as a fat arrow.

You may be wondering whether this is useful in the real world rather than just in made-up examples for the book. To show you concrete examples, I’ll use Noda Time.

Pass-through or delegating properties

We’ll briefly consider three types from Noda Time:

  • LocalDate—Just a date in a particular calendar with no time component
  • LocalTime—A time of day with no date component
  • LocalDateTime—The combination of a date and a time

Don’t worry about the details of initialization and so on; just think about what you’d want out of the three types. Obviously, a date will have properties for the year, month, and day, and a time will have hours, minutes, seconds, and so on. What about the type combining the two? It’s handy to be able to get the date and time components separately, but often you want the subcomponents of the date and time. Each implementation of LocalDate and LocalTime is carefully optimized, and I wouldn’t want to duplicate that logic in LocalDateTime, so the subcomponent properties are pass-throughs that delegate to properties of the date or time components. The implementation shown in the following listing is now extremely clean.

Listing 8.14. Delegating properties in Noda Time
public struct LocalDateTime
{
    public LocalDate Date { get; }          1
    public int Year => Date.Year;           2
    public int Month => Date.Month;         2
    public int Day => Date.Day;             2

    public LocalTime TimeOfDay { get; }     3
    public int Hour => TimeOfDay.Hour;      4
    public int Minute => TimeOfDay.Minute;  4
    public int Second => TimeOfDay.Second;  4

                                            5
}

  • 1 Property for the date component
  • 2 Properties delegating to date subcomponents
  • 3 Property for the time component
  • 4 Properties delegating to time subcomponents
  • 5 Initialization, other properties, and members

A lot of properties are like this; removing the { get { return ... } } part from each of them was a real pleasure and leaves the code much clearer.

Performing simple logic on another piece of state

Within LocalTime, there’s a single piece of state: the nanosecond within the day. All the other properties compute a value based on that. For example, the code to compute the subsecond value in nanoseconds is a simple remainder operation:

public int NanosecondOfSecond =>
    (int) (NanosecondOfDay % NodaConstants.NanosecondsPerSecond);

That code will get even simpler in chapter 10, but for now, you can just enjoy the brevity of the expression-bodied property.

Important caveat

Expression-bodied properties have one downside: there’s only a single-character difference between a read-only property and a public read/write field. In most cases, if you make a mistake, a compile-time error will occur, due to using other properties or fields within a field initializer, but for static properties or properties returning a constant value, it’s an easy mistake to make. Consider the difference between the following declarations:

// Declares a read-only property
public int Foo => 0;
// Declares a read/write public field
public int Foo = 0;

This has been a problem for me a couple of times, but after you’re aware of it, checking for it is easy enough. Make sure your code reviewers are aware of it, too, and you’re unlikely to get caught.

So far, we’ve concentrated on properties as a natural segue from the other new property-related features. As you may have guessed from the section title, however, other kinds of members can have expression bodies.

8.3.2. Expression-bodied methods, indexers, and operators

In addition to expression-bodied properties, you can write expression-bodied methods, read-only indexers, and operators, including user-defined conversions. The => is used in the same way, with no braces surrounding the expression and no explicit return statement.

For example, a simple Add method and its operator equivalent to add a Vector (with obvious X and Y properties) to a Point might look like the following listing in C# 5.

Listing 8.15. Simple methods and operators in C# 5
public static Point Add(Point left, Vector right)
{
    return left + right;                             1
}

public static Point operator+(Point left, Vector right)
{
    return new Point(left.X + right.X,               2
        left.Y + right.Y);                           2
}

  • 1 Just delegate to the operator.
  • 2 Simple constructor call to implement +

In C# 6, it could look simpler, with both of these being implemented using expression-bodied members, as in the next listing.

Listing 8.16. Expression-bodied methods and operators in C# 6
public static Point Add(Point left, Vector right) => left + right;

public static Point operator+(Point left, Vector right) =>
    new Point(left.X + right.X, left.Y + right.Y);

Note the formatting I’ve used in operator+; putting everything on one line would make it much too long. In general, I put the => at the end of the declaration part and indent the body as usual. The way you format your code is entirely up to you, but I’ve found this convention works well for all kinds of expression-bodied members.

You can also use expression bodies for void-returning methods. In that case, there’s no return statement to omit; only the braces are removed.

Note

This is consistent with the way lambda expressions work. As a reminder, expression-bodied members aren’t lambda expressions, but they have this aspect in common.

For example, consider a simple log method:

public static void Log(string text)
{
    Console.WriteLine("{0:o}: {1}", DateTime.UtcNow, text)
}

This could be written with an expression-bodied method like this instead:

public static void Log(string text) =>
    Console.WriteLine("{0:o}: {1}", DateTime.UtcNow, text);

Here the benefit is definitely smaller, but for methods where the declaration and body fit on one line, it can still be worth doing. In chapter 9, you’ll see a way of making this even cleaner using interpolated string literals.

For a final example with methods, a property, and an indexer, let’s imagine you want to create your own IReadOnlyList<T> implementation to provide a read-only view over any IList<T>. Of course, ReadOnlyCollection<T> already does this, but it also implements the mutable interfaces (IList<T>, ICollection<T>). At times you may want to be precise about what a collection allows via the interfaces it implements. With expression-bodied members, the implementation of such a wrapper is short indeed.

Listing 8.17. IReadOnlyList<T> implementation using expression-bodied members
public sealed class ReadOnlyListView<T> : IReadOnlyList<T>
{
    private readonly IList<T> list;                           

    public ReadOnlyListView(IList<T> list)
    {
        this.list = list;
    }
    public T this[int index] => list[index];          1
    public int Count => list.Count;                   2
    public IEnumerator<T> GetEnumerator() =>          3
        list.GetEnumerator();                         3
    IEnumerator IEnumerable.GetEnumerator() =>        4
        GetEnumerator();                              4
}

  • 1 Indexer delegating to list indexer
  • 2 Property delegating to list property
  • 3 Method delegating to list method
  • 4 Method delegating to the other GetEnumerator method

The only new feature shown here is the syntax for expression-bodied indexers, and I hope it’s sufficiently similar to the syntax for the other kinds of members that you didn’t even notice it was new.

Does anything stick out to you, though? Anything surprise you at all? That constructor looks a little ugly, doesn’t it?

8.3.3. Restrictions on expression-bodied members in C# 6

Normally, at this point, having just remarked on how verbose a piece of code is, I’d reveal the good news of another feature that C# has implemented to make it better. Not this time, I’m afraid—at least not in C# 6.

Even though the constructor has only a single statement, there’s no such thing as an expression-bodied constructor in C# 6. It’s not alone, either. You can’t have expression-bodied

  • Static constructors
  • Finalizers
  • Instance constructors
  • Read/write or write-only properties
  • Read/write or write-only indexers
  • Events

None of that keeps me awake at night, but the inconsistency apparently bothered the C# team enough that C# 7 allows all of these to be expression-bodied. They don’t typically save any printable characters, but formatting conventions allow them to save vertical space, and there’s still the readability hint that this is just a simple member. They all use the same syntax you’re already used to, and listing 8.18 gives a complete example, purely for the sake of showing the syntax. This code isn’t intended to be useful other than as an example, and in the case of the event handler, it’s dangerously non-thread-safe compared with a simple field-like event.

Listing 8.18. Extra expression-bodied members in C# 7
public class Demo
{
    static Demo() =>                                          1
        Console.WriteLine("Static constructor called");       1
    ~Demo() => Console.WriteLine("Finalizer called");         2

    private string name;
    private readonly int[] values = new int[10];

    public Demo(string name) => this.name = name;             3

    private PropertyChangedEventHandler handler;
    public event PropertyChangedEventHandler PropertyChanged  4
    {                                                         4
        add => handler += value;                              4
        remove => handler -= value;                           4
    }                                                         4

    public int this[int index]                                5
    {                                                         5
        get => values[index];                                 5
        set => values[index] = value;                         5
    }                                                         5

    public string Name                                        6
    {                                                         6
        get => name;                                          6
        set => name = value;                                  6
    }                                                         6
}

  • 1 Static constructor
  • 2 Finalizer
  • 3 Constructor
  • 4 Event with custom accessors
  • 5 Read/write indexer
  • 6 Read/write property

One nice aspect of this is that the get accessor can be expression-bodied even if the set accessor isn’t, or vice versa. For example, suppose you want to make your indexer setter validate that the new value isn’t negative. You could still keep an expression-bodied getter:

public int this[int index]
{
    get => values[index];
    set
    {
        if (value < 0)
        {
            throw new ArgumentOutOfRangeException();
        }
        Values[index] = value;
    }
}

I expect this to be reasonably common in the future. Setters tend to have validation, whereas getters are usually trivial, in my experience.

Tip

If you find yourself writing a lot of logic in a getter, it’s worth considering whether it should be a method. Sometimes the boundary can be fuzzy.

With all the benefits of expression-bodied members, do they have any other downsides? How aggressive should you be in converting everything you possibly can to use them?

8.3.4. Guidelines for using expression-bodied members

My experience is that expression-bodied members are particularly useful for operators, conversions, comparisons, equality checks, and ToString methods. These usually consist of simple code, but for some types there can be an awful lot of these members, and the difference in readability can be significant.

Unlike some features that are somewhat niche, expression-bodied members can be used to significant effect in pretty much every codebase I’ve come across. When I converted Noda Time to use C# 6, I removed roughly 50% of the return statements in the code. That’s a huge difference, and it’ll only increase as I gradually take advantage of the extra opportunities afforded by C# 7.

There’s more to expression-bodied members than readability, mind you. I’ve found that they provide a psychological effect: it feels like I’m doing functional programming to a greater extent than before. That, in turn, makes me feel smarter. Yes, that’s as silly as it sounds, but it really does feel satisfying. You may be more rational than me, of course.

The danger, as always, is overuse. In some cases, you can’t use expression-bodied members, because your code includes a for statement or something similar. In plenty of cases, it’s possible to convert a regular method into an expression-bodied member, but you really shouldn’t. I’ve found that there are two categories of members like this:

  • Members performing precondition checks
  • Members using explanatory variables

As an example of the first category, I have a class called Preconditions with a generic CheckNotNull method that accepts a reference and a parameter name. If the reference is null, it throws an ArgumentNullException using the parameter name; otherwise, it returns the value. This allows a convenient combination of check and assign statements in constructors and the like.

This also allows—but certainly doesn’t force—you to use the result as both the target of a method call or, indeed, an argument to it. The problem is, understanding what’s going on becomes difficult if you’re not careful. Here’s a method from the LocalDateTime struct I described earlier:

public ZonedDateTime InZone(
    DateTimeZone zone,
    ZoneLocalMappingResolver resolver)
{
    Preconditions.CheckNotNull(zone);
    Preconditions.CheckNotNull(resolver);
    return zone.ResolveLocal(this, resolver);
}

This reads nice and simply: check that the arguments are valid and then do the work by delegating to another method. This could be written as an expression-bodied member, like this:

public ZonedDateTime InZone(
    DateTimeZone zone,
    ZoneLocalMappingResolver resolver) =>
    Preconditions.CheckNotNull(zone)
        .ResolveLocal(
            this,
            Preconditions.CheckNotNull(resolver);

That would have exactly the same effect, but it’s much harder to read. In my experience, one validation check puts a method on the borderline for expression-bodied members; with two of them, it’s just too painful.

For explanatory variables, the NanosecondOfSecond example I provided earlier is just one of many properties on LocalTime. About half of them use expression bodies, but quite a few of them have two statements, like this:

public int Minute
{
    get
    {
        int minuteOfDay = (int) NanosecondOfDay / NanosecondsPerMinute;
        return minuteOfDay % MinutesPerHour;
    }
}

That can easily be written as an expression-bodied property by effectively inlining the minuteOfDay variable:

public int Minute =>
    ((int) NanosecondOfDay / NodaConstants.NanosecondsPerMinute) %
    NodaConstants.MinutesPerHour;

Again, the code achieves exactly the same goal, but in the original version, the minuteOfDay variable adds information about the meaning of the subexpression, making the code easier to read.

On any given day, I might come to a different conclusion. But in more complex cases, following a sequence of steps and naming the results can make all the difference when you come back to the code six months later. It also helps you if you ever need to step through the code in a debugger, as you can easily execute one statement at a time and check that the results are the ones you expect.

The good news is that you can experiment and change your mind as often as you like. Expression-bodied members are purely syntactic sugar, so if your taste changes over time, you can always convert more code to use them or revert code that used expression bodies a little too eagerly.

Summary

  • Automatically implemented properties can now be read-only and backed by a read-only field.
  • Automatically implemented properties can now have initializers rather than nondefault values having to be initialized in a constructor.
  • Structs can use automatically implemented properties without having to chain constructors together.
  • Expression-bodied members allow simple (single-expression) code to be written with less ceremony.
  • Although restrictions limit the kinds of members that can be written with expression bodies in C# 6, those restrictions are lifted in C# 7.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset