我的一位同事提出了一个有趣的想法,但我们对可能出现的并发症有点不确定。
目前,我们的大多数方法都有一个'out'参数来返回消息列表(成功,错误等)。像这样......
public bool Delete(int id, out List<UIMessage> uiMessages)
{
//Delete stuff
bool wasDeleteSuccessful = //set bool here
List<UIMessages> uiMessages = //Set messages here
return wasDeleteSuccessful
}
我们正在考虑返回一个具有T类型属性和List属性的新对象的想法。像这样......
public ResultObject<bool> Delete(int id)
{
//Delete stuff
bool wasDeleteSuccessful = //set bool here
List<UIMessages> uiMessages = //Set messages here
return new ResultObject<bool>(wasDeleteSuccessful, uiMessages)
}
我很确定这里唯一的好处是我们不必处理'out'参数,但是我们不考虑哪些缺点?
答案 0 :(得分:2)
通常最好返回值而不是变量变量。通过返回值而不是变量变量,您可以在不必改变变量的上下文中使用您的方法。例如:删除是异步的主要候选者,因为它可能是高延迟操作。改变调用者变量的方法非常难以异步。
然而,这是退一步的好时机,并询问你是否真的在做正确的事情。这种方法的合同似乎很奇怪。我期望一个删除某些东西的方法返回void,因为删除是一种效果,而不是一个值的产生。我希望在失败的情况下,失败状态将存储在抛出的异常中,而不是存储在消息列表中。
答案 1 :(得分:1)
没有主要缺点,只是新结果对象的不必要的额外复杂性,其中包含与先前使用out
参数的实现完全相同的信息。如果这些方法被执行了很多,你可能会更好(性能和内存方面),实例化的对象更少,因此你的第一个(out)实现。
我个人更喜欢给定示例中的out实现,因为您的方法往往遵循try-parse pattern。 try-parse模式通过返回bool告诉您成功,并在out参数中提供result / info对象。你的命名在这种情况下有点不对劲。而不是删除,最好将方法命名为TryDelete
答案 2 :(得分:0)
签出DomainResult NuGet软件包,该软件包提供了解决该问题的方法(需要.NET Standard 2)。
它的核心是IDomainResult
(类似于您的ResultObject
),其属性为:
IReadOnlyCollection<string> Errors { get; } // Collection of error messages if any
bool IsSuccess { get; } // Flag, whether the current status is successful or not
DomainOperationStatus Status { get; } // Current status of the domain operation: Success, Error, NotFound
从这里开始,您的方法中有2个用于返回对象的选项:
IDomainResult<T>
通过添加T Value { get; }
属性扩展了上述内容。(T, IDomainResult)
之类的方法中返回ValueTuple。所有这些都可以通过50多种扩展方法来实现,例如
// Successful result with an int
(value, state) = IDomainResult.Success(10); // value = 10; state.Status is 'Success'
// The same but wrapped in a task
var res = IDomainResult.SuccessTask(10); // res is Task<(int, IDomainResult)>
// Error message
IDomainResult = IDomainResult.Error("Ahh!"); // res.Status is 'Error' and res.Errors = new []{ "Ahh!" }
// Error when expected an int
(value, state) = IDomainResult.Error<int>("Ahh!"); // value = 0, state.Status is 'Error' and state.Errors = new []{ "Ahh!" }
示例为:
public async Task<(InvoiceResponseDto, IDomainResult)> GetInvoice(int invoiceId)
{
if (invoiceId < 0)
// Returns a validation error
return IDomainResult.Error<InvoiceResponseDto>("Try harder");
var invoice = await DataContext.Invoices.FindAsync(invoiceId);
if (invoice == null)
// Returns a Not Found response
return IDomainResult.NotFound<InvoiceResponseDto>();
// Returns the invoice
IDomainResult.Success(invoice);
}
或者如果您反对ValueTuple,则使用更传统的方法签名:
public async Task<IDomainResult<InvoiceResponseDto>> GetInvoice(int invoiceId)
{
if (invoiceId < 0)
// Returns a validation error
return DomainResult.Error<InvoiceResponseDto>("Try harder");
...
}
它还具有20多个扩展名,可以将基于IDomainResult
的类型转换为相应的IActionResult
,以便从WebAPI控制器方法返回。
在https://github.com/AKlaus/DomainResult处检查样本。都是你的。