问题
I am trying to find out the advantages and disadvantages for a method with multiple result values.
For example I'm using a login-method. If the login was successful, it will pass, otherwise I need to know why it failed.
1. Return true or false (Not enough information)
bool Login(string user, string password);
2. Return true, if it was successful, otherwise throw an exception
public class UnknownUserException : Exception { }
public class WrongPasswordException : Exception { }
bool Login(string user, string password);
3. Return nothing. Throw an exception, if it wasn't successful
public class UnknownUserException : Exception { }
public class WrongPasswordException : Exception { }
void Login(string user, string password);
4. Return an enum value
enum LoginResult
{
Successful
UnknownUser,
WrongPassword
}
LoginResult Login(string user, string password);
"Login" is just one example case. I would like to know what the advantages and disadvantages of the different implementations is, and for which cases they are more or less appropriate.
回答1:
Of course it depends on the certain situation but let me provide a couple of my subjective comments here:
I'm sure such cases should be avoided. The name of the method points that it just performs any operation like "Login". According to the method name we can't expect any result here. Is you'd like the method to return
boolvalue it's much better to name it likeIsLoggedIn(userName). In addition you'll never know if you need to extend the set of values being returned. So theenumis much better here also taking in account that the aim of the value is reflected in theenumname rather than simplebool.The same as above. Exceptions here help to stop the whole execution hierarchy (which can of course contain more than 1 method in call stack) instead of just returning the result and let the caller to make appropriate decision. More flexible solution as for me. In the current case I'd use exceptions for parameters validation only. Situations like "wrong user name/password" are not exceptional. They are normal from the use cases point of view.
nullparameter or wrong parameter format are exceptional cases.If you don't need the method to return a value, that is the way to go. Do not forget you shouldn't use exceptions as a navigation. I mean
UserSuccessfullyCreatedExceptionor so.As I mentioned above, that is the best way as for me. The single point is not to place validation exceptions as a
enumvalues. You have exceptions for it.
So enum result plus exceptions for validation is a way to go.
If you'd like to collect all the errors during the method execution you'll probably like to create special LoginOperationResult class to wrap all the information (including validation errors` occured during the method execution.
class OperationResult
{
public OperationStatus Status { get; set; }
public IEnumerable<ValidationError> Errors { get; set; }
// or list of exceptions
}
class LoginOperationResult : OperationResult
{
// Login result specific data.
}
enum OperationStatus
{
Success,
Denied,
ValidationFailed,
// etc.
}
回答2:
You'll get more opinionated answers, If I were doing I'll combine 3 & 4. Throw LoginFailedException with an enum saying why.
void Login(string user, string password);//Or return a bool(redundant though)
class LoginFailedException : ApplicationException
{
public LoginFailReason Reason {get; private set;}
public LoginFailedException(LoginFailReason reason)
{
this.Reason = reason;
}
}
enum LoginFailReason
{
UnknownUser,
WrongPassword
}
Reason to choose exception option: Assume you choose return value only approach, users of your api(may be client, or other developers) can get a chance to overlook the API.
instance.Login(user, password);
var accountInfo = instance.GetAccountInfo();//Assuming logged in; going to explode
Who knows they have to do like this
if(instance.Login(user, password) == LoginResult.Successful))
{
var accountInfo = instance.GetAccountInfo();
}
So, IMO throw exception in face saying I can't process your login request due to so and so reason. Make it simple.
回答3:
Definitely not exceptions. A failed login is hardly an "exceptional" case and it just a normal course of logic for the application. If you use an exception then you'll always have to wrap logging in with an exception handler for no other reason than to handle the case of a failed login. That seems like the very definition of using exceptions for logic flow, which isn't right.
If you need to return specific information (which isn't always necessary from a login function, but might be in your case), #4 seems reasonable. You could take it a step further and make it an object:
public class LoginResult
{
// an enum for the status
// a string for a more specific message
// a valid user object on successful login
// etc.
}
Or, depending on the logic for it, an immutable struct instead of a class. (Make sure the struct is immutable, mutable structs are just asking for trouble.) The point being that you can apply all sorts of logic and functionality on the result object itself, which seems to be the direction you're heading.
回答4:
I usually use this approach in my projects:
signature:
bool TryLogin(string username, string password, out User user);
usage:
User user;
if(userService.TryLogin(username, password, out user)))
{
// do stuff with user
}
else
{
// show "login failed"
}
You could expand this to return your Enum:
signature:
enum LoginResult
{
Successful
UnknownUser,
WrongPassword
}
LoginResult TryLogin(string username, string password, out User user);
usage:
User user;
LoginResult loginResult;
if((loginResult = userService.TryLogin(username, password, out user)) == LoginResult.Successful)
{
// do stuff with user
}
else
{
// do stuff with loginResult
}
来源:https://stackoverflow.com/questions/22807483/best-practice-return-value-vs-exception-vs-enum