Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int
:
int x;
x = 10;
В этом примере переменная x является int
, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.
Но когда вы пытаетесь объявить ссылочный тип, произойдет что-то другое. Возьмите следующий код:
Integer num;
num = new Integer(10);
Первая строка объявляет переменную с именем num
, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer
является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».
Во второй строке ключевое слово new
используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num
присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования .
(точка).
Exception
, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num
. Перед созданием объекта вы получите NullPointerException
. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.
Например, вы можете имеют следующий метод:
public void doSomething(SomeObject obj) {
//do something to obj
}
В этом случае вы не создаете объект obj
, скорее предполагая, что он был создан до вызова метода doSomething
. К сожалению, этот метод можно вызвать следующим образом:
doSomething(null);
В этом случае obj
имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException
, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.
Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething
может быть записано как:
/**
* @param obj An optional foo for ____. May be null, in which case
* the result will be ____.
*/
public void doSomething(SomeObject obj) {
if(obj != null) {
//do something
} else {
//do something else
}
}
Наконец, Как определить исключение & amp; причина использования Трассировки стека
Выполняя иначе простой метод «thunk», async создает в памяти состояние async state machine, а не async - нет. Хотя это часто может указывать на использование версии non-async, поскольку она более эффективна (это правда), это также означает, что в случае зависания у вас нет доказательств того, что этот метод задействован в «стеке возврата / продолжения», что иногда затрудняет понимание повесы.
Итак, да, когда perf не критичен (и обычно это не так), я брошу async на все эти методы thunk, чтобы у меня была машина состояния async, чтобы помочь мне диагностировать зависания позже, и также, чтобы гарантировать, что, если эти методы thunk когда-либо будут развиваться со временем, они обязательно вернут сбитые задачи вместо throw.
Это также смущает меня, и я чувствую, что предыдущие ответы не учитывали ваш фактический вопрос:
Зачем использовать конструкцию return wait, когда вы можете напрямую возвращать задачу из внутреннего вызова DoAnotherThingAsync ()?
blockquote>Ну, иногда вы на самом деле хотите
Task<SomeType>
, но в большинстве случаев вам действительно нужен экземплярSomeType
, то есть результат задачи.Из вашего кода:
async Task<SomeResult> DoSomethingAsync() { using (var foo = new Foo()) { return await foo.DoAnotherThingAsync(); } }
Человек, незнакомый с синтаксисом (например, я), может подумать, что этот метод должен возвращать
Task<SomeResult>
, но поскольку он помечен какasync
, это означает, что его фактический тип возвратаSomeResult
. Если вы просто используетеreturn foo.DoAnotherThingAsync()
, вы возвращаете задачу, которая не будет компилироваться. Правильный способ - вернуть результат задачи, поэтомуreturn await
.
var task = DoSomethingAsync();
вы получите задание, а не T
– Shoe
5 January 2017 в 18:50
async/await
. Насколько я понимаю, Task task = DoSomethingAsync()
, в то время как Something something = await DoSomethingAsync()
работают. Первая дает вам правильную задачу, а вторая из-за ключевого слова await
дает результат result i> из задачи после ее завершения. Я мог бы, например, иметь Task task = DoSomethingAsync(); Something something = await task;
.
– heltonbiker
5 January 2017 в 19:51
Единственная причина, по которой вы захотите это сделать, - это если в предыдущем коде есть еще один await
или если вы каким-то образом обработаете результат перед его возвратом. Другой способ, которым это может происходить, - это try/catch
, который изменяет порядок обработки исключений. Если вы ничего не делаете, тогда вы правы, нет причин добавлять накладные расходы на создание метода async
.
return await
необходимо (вместо того, чтобы просто возвращать задачу дочернего вызова) , даже если в предыдущем коде есть еще один ожидание. Не могли бы вы предоставить объяснение?
– TX_
30 September 2013 в 17:33
async
, то как бы вы ожидали первой задачи? Вам нужно отметить метод как async
, если вы хотите использовать any i>. Если метод отмечен как async
, и у вас есть await
ранее в коде, тогда вам нужно await
выполнить вторую операцию async для правильного типа. Если вы просто удалили await
, тогда он не будет компилироваться, так как возвращаемое значение не будет иметь правильный тип. Поскольку метод async
, результат всегда завернут в задачу.
– Servy
30 September 2013 в 17:38
async
вы не возвращаете задачу, вы возвращаете результат задачи, которая затем будет завернута.
– Servy
30 September 2013 в 20:21
Task<Type>
явно, а async
диктует возвратить Type
(который сам компилятор превратится в Task<Type>
).
– Noseratio
30 September 2013 в 20:33
async
является просто синтаксическим сахаром для явной проводки продолжений. Вы не нуждаетесь i> async
, чтобы что-либо сделать, но при выполнении практически нетривиальной асинхронной операции с резко i> легче работать. Например, код, который вы предоставили, на самом деле не распространяет ошибки, как вы этого хотели, и сделать это правильно в еще более сложных ситуациях становится намного сложнее. В то время как вы никогда не нуждаетесь i> async
, ситуации, которые я описываю, это то, где он добавляет ценность, чтобы использовать его.
– Servy
26 February 2016 в 03:33
Если вам не нужно async
(т. е. вы можете напрямую вернуть Task
), тогда не используйте async
.
Есть ситуации, когда return await
полезно, например, если у вас есть две асинхронные операции :
var intermediate = await FirstAsync();
return await SecondAwait(intermediate);
Дополнительные сведения о производительности async
см. в статье MSDN Стивена Тоуба и video по этой теме.
Обновление: я написал сообщение в блоге , которое идет гораздо подробнее.
await
полезен во втором случае? Почему бы не сделать return SecondAwait(intermediate);
?
– Matt Smith
30 September 2013 в 17:15
return SecondAwait(intermediate);
достичь цели в этом случае? Я думаю, return await
здесь лишний ...
– TX_
30 September 2013 в 17:31
await
в первой строке, вы должны использовать его и во втором.
– svick
30 September 2013 в 21:29
async
будет использовать ресурсы SecondAwait
является строкой, сообщение об ошибке: «CS4016: Поскольку это метод асинхронизации, выражение возврата должно быть type 'string' вместо 'Task & lt; string & gt;' & quot ;.
– svick
23 November 2016 в 13:17
Существует один подлый случай, когда return
в обычном методе и return await
в async
метод ведут себя по-другому: в сочетании с using
(или, в более общем смысле, любой return await
в блоке try
), .
Рассмотрим эти две версии метода:
Task<SomeResult> DoSomethingAsync()
{
using (var foo = new Foo())
{
return foo.DoAnotherThingAsync();
}
}
async Task<SomeResult> DoSomethingAsync()
{
using (var foo = new Foo())
{
return await foo.DoAnotherThingAsync();
}
}
Первый метод будет Dispose()
объектом Foo
, как только метод DoAnotherThingAsync()
вернется, что вероятно, задолго до его завершения. Это означает, что первая версия, вероятно, глючит (потому что Foo
находится слишком рано), а вторая версия будет работать нормально.
foo.DoAnotherThingAsync().ContinueWith(_ => foo.Dispose());
– ghord
23 September 2014 в 09:58
Dispose()
возвращает void
. Вам понадобится что-то вроде return foo.DoAnotherThingAsync().ContinueWith(t -> { foo.Dispose(); return t.Result; });
. Но я не знаю, почему бы вам это сделать, когда вы можете использовать второй вариант.
– svick
23 September 2014 в 10:05
{ var task = DoAnotherThingAsync(); task.ContinueWith(_ => foo.Dispose()); return task; }
. Вариант использования довольно прост: если вы используете .NET 4.0 (как и большинство), вы все равно можете написать асинхронный код таким образом, который будет хорошо работать из 4.5 приложений.
– ghord
23 September 2014 в 12:38
Foo
только после того, как завершено возвращенное Task
, что мне не нравится, потому что оно излишне вводит параллелизм.
– svick
23 September 2014 в 13:47