C# · 12月 27, 2021

从C#方法返回不同的类型

我有一个方法: public ??? AuthManager.Login(Credentials credentials)

以下是此方法的一组有效输出值:

>成功(accountId)
>失败:AccountLockedOut
> Failure:UsernameNotFound
>失败:InvalidPassword(尝试次数失败)

根据返回类型,不同的视图会显示给用户(是的,AccountLockedOut的视图不同于InvalidPassword).

我可以去

public class LoginAttemptResult { public bool Succeeded { get; set; } public AccountId AccountId { get; set; } // for when success public LoginAttemptResultEnumType Result { get;set; } // Success,Lockedout,UsernameNotFound,InvalidPassword public int FailedAttemptCount { get; set; } // only used for InvalidPassword}

我不喜欢这个,寻找更好的解决方案.首先,这导致部分初始化的对象,其中两个违反接口隔离原则,三个违反SRP.

更新:抛出异常也不是一个优雅的解决方案,因为我看到的InvalidPassword不是一个例外. DB连接失败是一个例外.空参数是一个例外. InvalidPassword是一个有效的预期响应.

我认为更好的解决方案是创建一个类的层次结构:

abstract class LoginAttemptResult sealed class LoginSuccess : LoginAttemptResult { AccountId } abstract class LoginFailure : LoginAttemptResult sealed class InvalidPasswordLoginFailure : LoginFailure { FailedAttemptCount } sealed class AccountLockedoutLoginFailure : LoginFailure

然后,Login方法的调用者将需要执行以下操作:

if (result is LoginSuccess) { …”welcome back mr. account id #” + (result as LoginSuccess).AccountId}else if (result is InvalidPasswordLoginFailure ) { …”you Failed ” + (result as InvalidPasswordLoginFailure).FailedAttemptCount + ” times”}

我没有看到任何错误(概念上)这种方法(除了它附带的一些类).

这种方法还有什么问题?

注意,这种方法本质上是一个F#的discriminated union (DU).

有更好的方法来建模吗?我已经有几个解决方案可行 – 现在我想要一个优雅的解决方案.

解决方法 如果结果类显着不同,您需要一个单独的类,我认为您的解决方案是可行的.但我不知道那个.为每个结果尝试此类: /// <summary>/// Immutable,created by the server/// </summary>class LoginResult{ /// <summary> /// Null in the case of failure /// </summary> public int? Id { get; private set; } /// <summary> /// Null in the case of success /// </summary> public string FailReason { get; private set; } /// <summary> /// Always >= 1 /// </summary> public int AttemptNumber { get; private set; } public LoginResult(int id,int attemptNumber) { Id = id; AttemptNumber = attemptNumber; } public LoginResult(string reason,int attemptNumber) { FailReason = reason; AttemptNumber = attemptNumber; }}

我可以想象,您的身份验证逻辑可能非常复杂,Id,FailReason和AttemptNumber不仅仅是您需要的属性.在这种情况下,您需要向我们介绍更具体的例子,我们将尝试构建适合您的逻辑的抽象,如果必要的话.在这种特殊情况下 – 抽象没有意义.