This is a question about C#'s extension methods and their design philosophy, so I think that the best way to answer this question is to quote MSDN's documentation on the purpose of extension methods:
Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type. For client code written in C#, F# and Visual Basic, there is no apparent difference between calling an extension method and the methods that are actually defined in a type.
In general, we recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type. For more information, see Inheritance.
When using an extension method to extend a type whose source code you cannot change, you run the risk that a change in the implementation of the type will cause your extension method to break.
If you do implement extension methods for a given type, remember the following points:
- An extension method will never be called if it has the same signature as a method defined in the type.
- Extension methods are brought into scope at the namespace level. For example, if you have multiple static classes that contain extension methods in a single namespace named
Extensions, they will all be brought into scope by the
using Extensions; directive.
To summarize, extension methods are designed to add instance methods to a particular type, even when the developers cannot do so directly. And because instance methods will always override extension methods if present (if called using instance method syntax), this should only be done if you cannot directly add a method or extend the class.*
In other words, an extension method should act just like an instance method would, because it may end up being made an instance method by some client. And because an instance method should throw if the object it's being called on is
null, so should the extension method.
*As a side note, this is exactly the situation that the designers of LINQ faced: when C# 3.0 was released, there were already millions of clients that were using
System.Collections.Generic.IEnumerable<T>, both in their collections and in
foreach loops. These classes returned
IEnumerator objects which only had the two methods
MoveNext, so adding any additional required instance methods, such as
Any, etc., would be breaking these millions of clients. So, in order to provide this functionality (especially since it can be implemented in terms of
MoveNext with relative ease), they released it as extension methods, which can be applied to any currently existing
IEnumerable instance and can also be implemented by classes in more efficient ways. Had C#'s designers decided to release LINQ on day one, it would have been provided as instance methods of
IEnumerable, and they probably would have designed some kind of system to provide default interface implementations of those methods.
null |> Seq.isEmptythrows
System.ArgumentNullException: Value cannot be null. The expectation seems to be that you won't be passing undefined values for something that is expected to exist, so it's still an exception when you have a null. If it's a matter of initialization, I would start with an empty sequence instead of a
nullwhen dealing with collections but instead use an empty collection in those cases.
Anyis just consistent.