When writing API methods that have arguments, keep the following two things in mind:
If arguments of a method are optional, overload the method so that the optional parameters don’t have to be passed and can instead use default values. For instance if you have the following method:
public void DoStuff(string arg1, string arg2, string arg3) { … }and arg3 is optional, overload DoStuff() so that it also includes a method like the following:
public void DoStuff(string arg1, string arg2)
{
DoStuff(arg1, arg2, “something”);
}
Of course the first DoStuff() method listed should allow null to be passed for the optional arg3 and handle it appropriately, but it’s also nice to have the overload so that the user can automatically defer to a default value for the optional argument.
If arguments are required and should not be null, validate the arguments and throw an error if a null value is passed in for one of these arguments. If you don’t do this checking, an error will be thrown later (probably a NullReferenceException), but the source of the error won’t be as obvious. You’re better off failing early and making the cause of the error more explicit. So at the start of the method, check all required arguments (that shouldn’t be null) for null values, and throw an ArgumentNullException if one of them is null. For instance, if the first two arguments of the DoStuff() method are required and shouldn’t be null, do the following:
public void DoStuff(string arg1, string arg2, string arg3)
{
if (arg1 == null)
throw new ArgumentNullException("arg1");
if (arg2 == null)
throw new ArgumentNullException("arg2");
// do stuff…
}
If an API method is overloaded, should all of the overloads do the null argument checking, or just one of them? A colleague of mine prefers to have all of the methods do the checking, as the top of the exception stack trace then shows the exact method where the error occurred. However, I prefer to have the one overloaded method that all the other overloads call do the checking, as it leads to less duplicate code. Sure, the exact method overload that originally received the null argument might not be at the top of the exception stack trace, but it’s not too hard to look a row or two down to find it. The .Net framework also follows this approach. Check out the File class’s overloaded WriteAllLines() methods:
public static void WriteAllLines(string path, string[] contents)
{
WriteAllLines(path, contents, StreamWriter.UTF8NoBOM);
}
public static void WriteAllLines(string path, string[] contents, Encoding encoding)
{
if (contents == null)
{
throw new ArgumentNullException("contents")
}
// write all of the lines...
}
The only downside that I see to this approach is that it could lead to scattered null checks if one of the overloaded methods takes additional required parameters that don't get passed to the other overloaded method. Alternately, Rick Brewster describes a fluent approach to parameter validation, which allows you to consolidate some of this argument checking while also reporting when multiple arguments are invalid (as opposed to the approach above, where only the first invalid argument is reported). Or you could utilize Spec# and its sweet non-null types:
public void DoStuff(string! arg1, string! arg2, string arg3) { ... }Writing API methods takes a little more consideration than stuff like private methods. However, following the practices outlined above will make your API much better to work with.