从可能执行异步调用的方法中轻松退出

huangapple go评论85阅读模式
英文:

Trivial exit from a method that may perform an asynchronous call

问题

我遇到了一个问题,调用一个方法,该方法可能根据初始同步查找调用的结果返回一个 Task<T>null(这本身可能是一个反模式,请告诉我)。我想在发生微不足道的退出条件时返回 null,但这会导致外部调用方法失败,因为外部调用期望一个 Task<T> 响应(微不足道的退出条件),该响应通过 ConfigureAwait(true) 传递,随后产生 NullReferenceException

外部调用方法:

var res = await MyService.GetUserCourseStatusAsync(userID, productID).ConfigureAwait(true);

中间方法:

public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
    // 查找用户的ID(如果存在)
    var userCredentials = GetUserCredentials(userID);

    if (userCredentials?.UserID == null)
        return null; // 微不足道的返回null("用户未注册")。 *** 这会导致上面的ConfigureAwait(true)抛出异常 ***

    // 另一个同步调用
    var courseId = GetCourseID(productID);

    if (courseId == null)
        throw new InvalidOperationException($"产品 #{productID} 不是课程");

    // 对内部方法进行异步调用(这部分工作正常)
    return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}

因此,我的想法是我们应该始终返回 Task<T> 而不是 null
然而,所有这些都会引发编译错误:

//return null; // 微不足道的返回null("用户未注册")。 *** 这会导致异常 

// 编译错误:CS0029:无法将类型'GetUserCourseInner'隐式转换为'System.Threading.Tasks.Task<IGetUserCourseResponse>'
return new GetUserCourseInner(); // 未注册

// 编译错误:CS1503 参数1:无法将类型'GetUserCourseInner'转换为'System.Func<IGetUserCourseResponse>'
return new Task<IGetUserCourseResponse>(new GetUserCourseInner()); // 未注册

我该如何返回一个不是异步调用结果的虚拟 Task<T>
这甚至是正确的方法吗?

英文:

I've got an issue calling a method that may return a Task<T> or null depending on the result of an initial synchronous lookup call (this itself might be an anti-pattern so please let me know).

I kind of want to return null if a trivial exit condition occurs but this is causing the calling (outer) method to fail because the outer call expects a Task<T> response (trivial exit) which gets pushed through to ConfigureAwait(true) which subsequently produces a NullReferenceException.

Outer calling method:

var res = await MyService.GetUserCourseStatusAsync(userID, productID).ConfigureAwait(true);

Intermediate method:

public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
	// Look up User's ID (if exists)
	var userCredentials = GetUserCredentials(userID);

	if (userCredentials?.UserID == null)
		return null; // Trivial return of null ("User not registered"). *** This causes exception on ConfigureAwait(true) above ***

	// Another synchronous call
	var courseId = GetCourseID(productID);

	if (courseId == null)
		throw new InvalidOperationException($"Product #{productID} is not a course");

	// Asynchronous call to inner method (this bit works fine)
	return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}

So my thought then is that we should always return a Task<T> instead of null.
However, all of these cause compile errors:

//return null; // Trivial return of null ("User not registered"). *** This causes exception 

// Compile error: CS0029: Cannot implicitly convert type 'GetUserCourseInner' to 'System.Threading.Tasks.Task<IGetUserCourseResponse>'
return new GetUserCourseInner(); // Not registered

// Compile error: CS1503	Argument 1: cannot convert from 'GetUserCourseInner' to 'System.Func<IGetUserCourseResponse>'
return new Task<IGetUserCourseResponse>(new GetUserCourseInner()); // Not registered

How do I return a dummy Task<T> that isn't a result of a async call?

Is this even the correct approach?

答案1

得分: 4

以下是您要翻译的内容:

原文:
It would be better, as you suggested, to return a Task<IGetUserCourseResponse> which contains null (or some other sentinel value). You can create such a completed Task with Task.FromResult((IGetUserCourseResponse)null):

翻译:
正如您建议的那样,最好返回一个包含 null(或其他标记值)的 Task<IGetUserCourseResponse>。您可以使用 Task.FromResult((IGetUserCourseResponse)null) 创建这样一个已完成的 Task

原文:
public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// Look up User's ID (if exists)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return Task.FromResult((IGetUserCourseResponse)null);

// Another synchronous call
var courseId = GetCourseID(productID);
if (courseId == null)
    throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

// Asynchronous call to inner method (this bit works fine)
return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);

}

翻译:
public Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// 查找用户的ID(如果存在)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return Task.FromResult((IGetUserCourseResponse)null);

// 另一个同步调用
var courseId = GetCourseID(productID);
if (courseId == null)
    throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

// 对内部方法进行异步调用(这部分工作正常)
return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);

}

原文:
Alternatively, you could make your outer method async. Note however that this changes its behaviour in the case where it throws an InvalidOperationException: instead of the method throwing this exception directly, it will instead return a Task which contains this exception. This may or may not be what you want:

翻译:
或者,您可以将外部方法标记为 async请注意,在抛出 InvalidOperationException 的情况下,这将更改其行为:而不是方法直接抛出此异常,而是返回包含此异常的 Task。这可能或可能不是您想要的

原文:
public async Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// Look up User's ID (if exists)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return null;

// Another synchronous call
var courseId = GetCourseID(productID);
if (courseId == null)
    throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

// Asynchronous call to inner method (this bit works fine)
return await GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);

}

翻译:
public async Task<IGetUserCourseResponse> GetUserCourseStatusAsync(int userID, int productID)
{
// 查找用户的ID(如果存在)
var userCredentials = GetUserCredentials(userID);
if (userCredentials?.UserID == null)
return null;

// 另一个同步调用
var courseId = GetCourseID(productID);
if (courseId == null)
    throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

// 对内部方法进行异步调用(这部分工作正常)
return await GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);

}

英文:

It would be better, as you suggested, to return a Task&lt;IGetUserCourseResponse&gt; which contains null (or some other sentinel value). You can create such a completed Task with Task.FromResult((IGetUserCourseResponse)null):

public Task&lt;IGetUserCourseResponse&gt; GetUserCourseStatusAsync(int userID, int productID)
{
    // Look up User&#39;s ID (if exists)
    var userCredentials = GetUserCredentials(userID);
    if (userCredentials?.UserID == null)
        return Task.FromResult((IGetUserCourseResponse)null);

    // Another synchronous call
    var courseId = GetCourseID(productID);
    if (courseId == null)
        throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

    // Asynchronous call to inner method (this bit works fine)
    return GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}

Alternatively, you could make your outer method async. Note however that this changes its behaviour in the case where it throws an InvalidOperationException: instead of the method throwing this exception directly, it will instead return a Task which contains this exception. This may or may not be what you want:

public async Task&lt;IGetUserCourseResponse&gt; GetUserCourseStatusAsync(int userID, int productID)
{
    // Look up User&#39;s ID (if exists)
    var userCredentials = GetUserCredentials(userID);
    if (userCredentials?.UserID == null)
        return null;

    // Another synchronous call
    var courseId = GetCourseID(productID);
    if (courseId == null)
        throw new InvalidOperationException($&quot;Product #{productID} is not a course&quot;);

    // Asynchronous call to inner method (this bit works fine)
    return await GetUserCourseAsync(userCredentials.UserID.Value, courseId.Value);
}

答案2

得分: 3

返回一个包含空值作为结果的任务。

return Task.FromResult<IGetUserCourseResponse>(null);
英文:

Just return a Task that holds a null value as result

return Task.FromResult&lt;IGetUserCourseResponse&gt;(null);

huangapple
  • 本文由 发表于 2020年1月6日 22:56:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/59614265.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定