这个问题类似于这个问题的(一个子集)
在这种情况下,它使用运行时类型来区分成功和失败返回的结果。
我经常看到以下模式:
public struct Result {
public boolean IsSuccess { get;set;}
public string ErrorMessage {get;set;}
public int Value {get;set;}
}
...
Result result = someObject.SomeMethod();
if (result.IsSuccess) DoSomething(result.Value);
else handleError(result.ErrorMessage);
我认为以下更自然,并且更清楚地表达了意图(在我看来):
public abstract class Result { }
public sealed class Failure : Result {
public string ErrorMessage { get; set; }
}
public sealed class Success : Result {
public int Value { get; set; }
}
...
Result result = someObject.SomeMethod();
if (result is Success) DoSomething((result as Success).Value);
else if (result is Failure) handleError((result as Failure).ErrorMessage);
另请注意,.Net(和许多其他语言)在带有多个 catch 子句的 try-catch 块中使用此模式(其中异常类型选择一个 catch 块)。
编辑:这种模式(即依赖运行时类型)与 F# 的可区分联合相同,不同之处在于在 F# 中它是本机的,而在 C# 中它是使用用于不同目的的构造来模拟的。
编辑:我认为我的第一个代码的主要问题是代码气味“部分初始化的对象”。在 100% 的情况下,只有一半的对象会被初始化。它也几乎违反了 ISP,“几乎”因为一旦 .IsSuccess 被评估,以后只会使用对象的一部分(如果成功,则仅使用 .Result,如果是错误 - 仅使用错误属性)。运行类型检查解决方案没有这些问题。
所以问题是:使用这种模式有什么问题?我对以下几个方面的问题特别感兴趣:可维护性、可读性、可测试性、概念纯度和 OOP/OOD。