Здравствуйте, vaa, Вы писали: vaa>потому что странная логика, у меня в голове не укладывается.
Вам нужно разобраться с асинком. То, что он не укладывается в голове — это недостаток подготовки. vaa>или пусть хотя бы компайлер не ругается.
Компайлер говорит вам о том, что ваш метод — очень подозрительный. Он претендует на асинхронность, но ничего асинхронного не делает.
Зачем вам нужен такой метод?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vaa, Вы писали:
vaa>почему можно написать так: vaa>
vaa> async Task Do() {}
vaa>
vaa>, но компайлер будет ругаться vaa>и нельзя так: vaa>
vaa>Task Do() {}
vaa>
Асинхронный метод должен возвращать какую-либо Task, выполнение которой потом будут ждать в await. Причём сам механизм асинхронности работает хитро — таска создаётся и возвращается автоматически, по сути, до выполнения метода, а сам метод потом работает как обычный (ему пофиг, что он завёрнут в Task). По сути, твой первый метод — это обычный void метод, у которого не обязательно писать return.
"Идеально правильно" его было бы записать вот так:
async Task<void> Do() {}
Но void не используется в generic, и поэтому пишут просто Task, чтобы не было ошибок компиляции. Ругается в нём компилятор на то, что внутри асинхронного метода нет await и сразу предупреждает, что, фактически, метод будет выполняться синхронно, не смотря на все асинки.
А второй метод — это обычный (не асинхронный) метод, который возвращает Task. Никакой обёртки над методом нет, ты просто должен вернуть какой-либо Task и, соответственно, компилятор ожидает return этой таски.
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, vaa, Вы писали:
vaa>>почему можно написать так: vaa>>
vaa>> async Task Do() {}
vaa>>
vaa>>, но компайлер будет ругаться vaa>>и нельзя так: vaa>>
vaa>>Task Do() {}
vaa>>
S>Во втором случае метод должен вернуть Task, поэтому правильно не компилируется. Почему компилируется первый метод-- без понятия. Ну т.е., видимо, в первом методе не обязательно что-либо возвращать.
в первом компилятор сам вставит код который таск вернет
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Здравствуйте, vaa, Вы писали:
vaa>почему можно написать так: vaa>
vaa> async Task Do() {}
vaa>
vaa>, но компайлер будет ругаться vaa>и нельзя так: vaa>
vaa>Task Do() {}
vaa>
Во втором случае метод должен вернуть Task, поэтому правильно не компилируется. Почему компилируется первый метод-- без понятия. Ну т.е., видимо, в первом методе не обязательно что-либо возвращать.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, vaa, Вы писали:
vaa>>почему можно написать так: vaa>>
vaa>> async Task Do() {}
vaa>>
vaa>>, но компайлер будет ругаться vaa>>и нельзя так: vaa>>
vaa>>Task Do() {}
vaa>>
НС>Потому что такова логика работы async. Почему у тебя такой вопрос возникает?
потому что странная логика, у меня в голове не укладывается. или пусть хотя бы компайлер не ругается.
Здравствуйте, vaa, Вы писали:
НС>>Потому что такова логика работы async. Почему у тебя такой вопрос возникает? vaa>потому что странная логика, у меня в голове не укладывается. или пусть хотя бы компайлер не ругается.
Логика абсолютно тривиальная. А компайлер ругается, потому что код с практической точки зрения бесполезен. Вырожденные случаи не могут быть основанием для специальных исключений из правил, ибо бесполезно усложняют спецификацию и компилятор.
Здравствуйте, Sinclair, Вы писали:
S>Компайлер говорит вам о том, что ваш метод — очень подозрительный. Он претендует на асинхронность, но ничего асинхронного не делает. S>Зачем вам нужен такой метод?
Здравствуйте, vaa, Вы писали:
vaa>почему можно написать так: vaa>
vaa> async Task Do() {}
vaa>
vaa>, но компайлер будет ругаться vaa>и нельзя так: vaa>
vaa>Task Do() {}
vaa>
Почему сразу плохо-то?
Вот так нельзя
Task Do() {}
ровно по той же причине, почему нельзя вот так
int Do() {}
всё логично.
А вот это на самом деле генерирует некоторое количество кода, включая конечный автомат, и возвращает таску, в которую "завёрнут" результат выполнения метода (в данном случае void).
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Sinclair, Вы писали:
S>>Компайлер говорит вам о том, что ваш метод — очень подозрительный. Он претендует на асинхронность, но ничего асинхронного не делает. S>>Зачем вам нужен такой метод?
НС>Кейс то можно придумать. НС>
Здравствуйте, Sinclair, Вы писали:
S> Task Do() => DoCore(); // тот же результат, только без лишней task, которая ждёт task.
Там может и сложнее логика быть, не из одной строчки. Это же схематический пример.
S> Task Do() {_logger.LogInfo("Do called"); return Task.CompletedTask}; // Fine!
Здравствуйте, ksg71, Вы писали:
S>>Во втором случае метод должен вернуть Task, поэтому правильно не компилируется. Почему компилируется первый метод-- без понятия. Ну т.е., видимо, в первом методе не обязательно что-либо возвращать. K>в первом компилятор сам вставит код который таск вернет
Ага, вот такая штука будет скомпилирована:
[CompilerGenerated]
private sealed class <Do>d__1 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public C <>4__this;
private void MoveNext()
{
int num = <>1__state;
try
{
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<>t__builder.SetResult();
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNextthis.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachinethis.SetStateMachine(stateMachine);
}
}
[AsyncStateMachine(typeof(<Do>d__1))]
[DebuggerStepThrough]
private Task Do()
{
<Do>d__1 stateMachine = new <Do>d__1();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
Здравствуйте, Ночной Смотрящий, Вы писали: НС>Там может и сложнее логика быть, не из одной строчки. Это же схематический пример.
Ок, я просто думал речь о делегировании во что-то S>> async Task Do() { await _logger.LogInfo("Do called")} // Fine! НС>LogInfo синхронный, его нельзя await.
Тогда только первый вариант, да.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vaa, Вы писали:
vaa>есть возвращаемый тип, но ничего не возвращаем. такс слово лишнее. vaa>
vaa>async Do() {}
vaa>
vaa>было бы намного логичнее в этом случае. vaa>void же нас не удивляет.
Если вы пришли из функционального языка, то сигнатура функции это первое что на что нужно обращать внимание.
Потому что именно знание сигнатуры функции позволяет их комбинировать.
async вообще ортогонален сигнатуре. это больше про сахар, который разрешит вам использовать await конструкции в теле функции и позволит записать стейт-машину в процедурном стиле.
Есть у меня несколько знакомых "новичков" в программировании, вот там тенденция, что все методы имеют типа void -> void, а весь стейт находится в полях объекта/структуры.
Т.е. для них функция/метод это просто "именнованный" кусок кода.
И да, для них "очевидно" что если тип не указан, то это должен быть void. Это ж самый распрострененный тип возврата.
Отсылка к функциональным языкам обычно что-то меняет в головах.
Но я в первый раз вижу что бы пришедший из функционального языка говорил, что пропуск типа в сигнатуре это нормально.
Здравствуйте, vaa, Вы писали:
vaa>неужели вы не видите, что это нарушает ранее созданные правила? vaa>есть возвращаемый тип, но ничего не возвращаем. такс слово лишнее.
async Task<int> Do() { return 5; }
А вот такое несоответствие не смущает? Обещали Task<int>, возвращаем просто int.
vaa>
vaa>async Do() {}
vaa>
vaa>было бы намного логичнее в этом случае. vaa>void же нас не удивляет.
Но ведь на самом деле метод вернет именно таску и без использования await её мы и получим. А такая сигнатура будет только еще более нелогичной.
Исходно проблема в возможности вызвать метод с await или без него, что, по сути, меняет тип возвращаемого результата. Идеально в данном случае не сделать, кмк.
Здравствуйте, vaa, Вы писали:
vaa>неужели вы не видите, что это нарушает ранее созданные правила? vaa>есть возвращаемый тип, но ничего не возвращаем. такс слово лишнее.
async/await — синтаксический сахар со своими нюансами.
Тут уже придётся принять их или не использовать.
Наличие/отсутствие async не меняет сигнатуру метода.
Было бы не менее странно, если бы для реализации интерфейса:
interface IA
{
Task Do();
}
был написан такой класс:
class A: IA
{
async Do() {}
}
и непонятно что потом делать с таким классом:
class A2: IA
{
async Do() {}
Task Do() { ... }
}
в итоге бы началось такое безудержное веселье, если бы они решили не нарушать "ранее созданные правила"...