I often use the pattern
bool TryGetX(out X x)
when I need to get data from a service that may fail. I use this when neither the client nor the service can know if the data is available until trying to retrieve it. I've never been totally happy with using the out parameter that way, and I see from other SO questions that it is considered a poor pattern.
However, I haven't seen a convincing alternative API that gives the client a hint that their method call may not actually return the data.
Alternatives I've considered:
X GetX(), where GetX() returns null if the service cannot provide the requested data. I don't like doing this since it isn't clear to many people that they need to check for null being returned. Given the number of null reference exceptions I encounter in legacy code I've worked on, people often neglect a null-check and the user gets an ugly exception box shown to them. At least the TryGetX approach is clear to the client that the data may not be returned. Even if they do check for null, is requiring a if (x == null) everywhere really better than TryGetX(out X x)?
Try/Catch, where GetX() throws an exception if the data cannot be returned. Again, clients may not realize that GetX() throws an exception, and even if they do, you have Try/Catches sprinkled around the code.
GetXResult GetX(), where the class GetXResult has two properties: bool Success and X x. This would probably be my preferred alternative, but then I have many result classes floating around, cluttering my project.
What is the best approach for a method that returns data, but may not be able to return the data requested?
Best approach depends on the problem, I doubt that there is a 'best' approach that fits all the problems.
X GetX(), where GetX() returns null if the service cannot provide the requested data.
If all of the types that the service needs to return are Nullable and there can be kind of consistency with all these type of methods, this is not a bad approach at all. In order to clarify the 'null' return to clients, you may include 'Try' in method name (documentation also helps clients).
Try/Catch, where GetX() throws an exception if the data cannot be returned.
Throwing or not throwing exceptions is a decision that should be made in service architecture. If you choose to throw exceptions in that level for all your methods that need to, this approach also works fine. (use documentation to inform clients).
GetXResult GetX(), where the class GetXResult has two properties: bool Success and X x.
In orderto solve the 'too many classes' problem simply use generics:
sealed class Result<T> {
private bool success = false;
private T value;
private Result() {
}
public Result(T value) {
this.success = true;
this.value = value;
}
public static Result<T> Failure() {
return new Result<T>();
}
public bool Success {
get {
return this.success;
}
}
public T Value {
get {
if (!this.success) {
throw new InvalidOperationException();
}
return this.value;
}
}
}