How to tell whether an extension method affects 'this' or not?

452 views Asked by At

I know that String extension methods return a String and do not actually affect the variable calling the extension method (so it's immutable) - but how do I tell whether other extension methods do or not? For example, I'm working with a List<NewsItem> - and I need to order this list by date descending, so I've written this code:

newsItems.OrderByDescending(o => o.Date);

Does this affect the newsItems list or just return the IOrderedEnumerable??

In other words, should the above code actually read:

newsItems = newsItems.OrderByDescending(o => o.Date).ToList();

??

Thanks,

Dan

3

There are 3 answers

5
Jon Skeet On BEST ANSWER

OrderByDescending is an extension method on IEnumerable<T>, which itself provides read-only access. Of course, the extension method could cast to List<T>, but basically none of the LINQ to Objects extension methods affect their target (assuming the targets aren't affected by iteration, of course).

LINQ is designed in a functional style - methods return a new view on the data rather than changing the target they're called on.

EDIT: As noted by drew, the [Pure] attribute can be used as an indication of this, and some tools will pick up on it. However:

  • You still need to trust that the [Pure] attribute has been correctly applied
  • It's not always easy to tell whether or not there is a [Pure] attribute (it doesn't show up in the docs I linked to, for example)
  • You should probably be reading the documentation for anything you call anyway, if you aren't confident in what it does
0
Drew Noakes On

All of the Linq extension methods only perform query operations over sequences, so assume that they're read only (queries). So, they return IEnumerable<T> instances that provide sequences of values representing the result of the operation.

Of course there's nothing stopping you making an extension method that modifies the object to which it's applied (this).

The API contains metadata that is useful in this case however. The PureAttribute attribute can be used on method that have no side effects. If you use a tool such as ReSharper in your IDE, then it will warn you when you try to call a pure method and do not use the result. Because such a method is pure, it has no visible side effects elsewhere in memory, and so ignoring the result is a bit daft. The Linq operators are annotated with this attribute. So it object.ToString() for example. You can also use this attribute on your own code.

tl;dr R# would have found your bug in this case.

0
Dr. Andrew Burnett-Thompson On

Generally by the parameter (value type or reference type) and secondly by the return type.

If you have a method signature such DateTime DateTime.AddDays(int days) you are getting two hints that the method does not affect the original datetime - firstly that DateTime is a value type, secondly that it returns a DateTime. Value types, if modified in the method body will only have their changes applied locally to the copy on the stack. Unless the value type is returned the caller can never observe those changes. Reference types by contrast can be modified in a calling method and have the modifications observed by the caller.

Similarly if you have a method such as IOrderedEnumerable<T> OrderBy(IEnumerable<T> enumerable), despite the fact that the input type is a reference type (and can be modified), the hint is that as a new type is returned the original will remain unmodified.

Finally, this can be confirmed through testing. As a general point of interest the LINQ method chain extension methods all return a new IEnumerable<T>, are lazy evaluated and don't modify the contents IEnumerable<T> passed in, instead applying a filter which is evaluated when GetEnumerator is called on the the returned IEnumerable<T>.