我有一个异步方法,它将通过Api查找作业调度服务的jobId。
如果找不到结果,最好返回一个空任务还是null?
据我所知,当返回一个集合时,最好返回一个空集合而不是null并使用对象最好返回null而不是空对象;但对于任务我不确定哪个是最好的。 见附件。
谢谢
public virtual Task<int> GetJobRunIdAsync(int jobId)
{
var jobMonRequest = new jobmonRequest(true, true, true, true, true,
true, true, true, true, true, true, true,
true,
true, true, true, DateTime.Today, jobId, null, 0, null, null,
null, null, 0, 0);
var jobMonResponseTask = Client.jobmonAsync(jobMonRequest);
var jobTask = jobMonResponseTask.ContinueWith(task =>
{
if (jobMonResponseTask.Result == null )
{
var empty = new Task<int>(() => 0); // as i understand creating a task with a predefined result will reduce overhead.
return empty.Result; // || is it better to just return null?
}
if (jobMonResponseTask.Result.jobrun.Length > 1)
{
throw new Exception("More than one job found, Wizards are abound.");
}
return jobMonResponseTask.Result.jobrun.Single().id;
});
return jobTask;
}
答案 0 :(得分:7)
如果找不到结果,最好返回一个空任务还是null?
这里有几件事需要考虑:
首先,你应该永远不会返回null Task
。在async
世界中,null
任务只是没有意义。 Task
表示异步方法的执行,因此对于返回null
任务的异步方法,就像告诉调用代码“你当然没有真正调用此方法”当然。
因此,从方法返回的Task
/ Task<T>
永远不应该是null
。但是,您仍然可以选择在常规任务中返回null
值。这取决于你。
我不确定哪个是最好的任务。
任务只是一个包装器。底层逻辑仍然相同。想想这个方法如果是同步的,它会是什么样的;如果找不到任何内容,您的退货类型会为int
并返回0
,或者如果找不到任何内容,您的退货类型会为int?
并返回null
吗?在为同步方法做出选择之后,将其包装在Task<T>
中以用于异步方法。
作为最后一点,我必须说:
Task
constructor。Task<T>.Result
; use await
instead。ContinueWith
; use await
instead。您的方法可以大大简化:
public virtual async Task<int> GetJobRunIdAsync(int jobId)
{
var jobMonRequest = ...;
var jobMonResponse = await Client.jobmonAsync(jobMonRequest);
if (jobMonResponse == null)
return 0;
if (jobMonResponse.jobrun.Length > 1)
throw new Exception("More than one job found, Wizards are abound.");
return jobMonResponse.jobrun.Single().id;
}
或者,如果您想要返回null
的值(非任务):
public virtual async Task<int?> GetJobRunIdAsync(int jobId)
{
var jobMonRequest = ...;
var jobMonResponse = await Client.jobmonAsync(jobMonRequest);
if (jobMonResponse == null)
return null;
if (jobMonResponse.jobrun.Length > 1)
throw new Exception("More than one job found, Wizards are abound.");
return jobMonResponse.jobrun.Single().id;
}
答案 1 :(得分:3)
Stephen Cleary 的回答完美地解释了这一点:永远不要返回 string?
,否则会引发空引用异常,但我想补充一点:
null
,则返回已完成的任务,这可以通过返回 Task.CompletedTask
来完成Task
,则返回 Task<T>
的已完成任务,这可以使用 Task.FromResult<TResult>(TResult)
完成如果返回 null 而不是已完成的任务,此代码将抛出 null 引用异常:
T
即使您在调试器中看到它,也很难理解发生了什么。
因此,从不,从不,从返回 await FunctionThatShouldRetunrTaskButReturnsNull();
的 非null
函数返回 async
。
说明:
Task
或 async
的非 Task
函数中,您需要显式创建任务,并且存在返回 Task<T>
的危险一个任务。null
或 async
的 Task
函数中,您只需返回或返回一个值,并且函数的结果被隐式转换为任务,因此有没有返回 Task<T>
的危险。答案 2 :(得分:1)
如果您确实要从异步方法返回null,则可以使用Task.FromResult(null)
例如:
public async Task<FileInfo> GetInfo()
{
return await Task.FromResult<FileInfo>(null);
}
答案 3 :(得分:0)
这里有两点。首先是没有有效结果的返回值。我会将返回类型更改为int?并为jobRun Id返回null,以向调用者指示没有有效值。
此处详细介绍了如何返回任务的另一部分If my interface must return Task what is the best way to have a no-operation implementation?
答案 4 :(得分:0)
我个人的偏好是尽可能避免null
。这会强制调用者实现检查返回值并减少无意的NullReferenceException
。
我唯一一次使用null
是为了返回值类型。可以为空的值类型提供HasValue
和Value
属性,以便调用者可以执行以下操作:
var jobId = api.GetJobRunIdAsync(1234).Result; //note: highly recommend using async/await here instead of just returning a task
if(jobId.HasValue)
{
var result = DoSomethingWithId(jobId);
//continue processing...
}
我认为这会在您提供的示例中很有效,因为您将返回int
。
返回集合时,我更喜欢空集合而不是空对象。这需要更少的分支,这使代码更容易阅读和测试 - 如果返回null集合,您最终会得到类似的东西:
var results = await GetResultsForJobId(1234);
if(results != null) {}
// or results?.SomeLinqOperation();
空集合只是
var results = await GetResultsForJobId(1234);
results.SomeLinqOperation();
对于其他非集合引用类型,我建议实现Maybe<T>
或Optional<T>
,它可以与Nullable<T>
类似的方式与引用类型一起使用。可以在https://github.com/nlkl/Optional的GitHub上找到一个这样的实现的示例。更简单的版本可能是:
public struct Optional<T>
{
private static readonly Optional<T> _readOnlyEmpty = new Optional<T>();
public static Optional<T> Empty => _readOnlyEmpty;
public T Value { get; }
public bool HasValue { get; private set; }
public Optional(T value)
: this()
{
Value = value;
HasValue = true;
}
public static implicit operator Optional<T>(T value)
{
return new Optional<T>(value);
}
public static implicit operator T(Optional<T> optional)
{
return optional.Value;
}
}
答案 5 :(得分:0)
最好返回一个空集合,而不是null
,因为集合通常会实现IEnumerable
,因此将通过foreach(var item in collection)
进行迭代。
如果集合对象是NullReferenceException
而不是空集合,则Foreach将遇到null
。返回一个空集合将避免程序崩溃,因为在这种情况下忘记了空引用检查,并且更简单。
有时,只有在需要时才能创建集合对象,例如通过yield
语句。在此之前,结果根本就不存在,因此null可能在这里存在问题,因此返回空集合而不是null在这里是有意义的。
但是,如果返回单个对象,则返回null是完全有效的,如果对象所代表的实体可能不存在,并且可能与空对象(可能具有初始化为默认值的属性等)不同。如果它可能失败,你将需要检查它是否确实失败了,所以在这种情况下null不是问题,事实上如果引用可能没有任何表示,那么就是所希望的结果。
至于Task
。 Task
本身是检查或等待其完成所必需的。如果您需要确保Task
已完成,请返回空Task
。如果你不需要检查Task
完成并且只是解雇并忘记它,那么重新调整任何东西的目的是什么?