#c#
#c#
Вопрос:
У меня есть эта функция с параметром action, после ее выполнения параметр в этой функции будет иметь значение. Есть ли способ для меня получить значение?
public void DetailsAsync(string param1, string param2,Action<IList<Detail>> callback)
{
//process happen here and will have a callback to produce the data for detail
}
public class DetailController:ApiController
{
private IList<Detail> details;
private DetailCompleted(IList<Detail> detail)
{
//now detail parameter has a value that I can use
details = detail;
}
[HttpGet]
public IList<Detail> GetDetails()
{
ServiceManager.DetailsAsync("param1","param2",detailsCompleted)
//after ServiceManager.DetailsAsync it will go to return details
return details;
}
}
Когда я пробовал этот код, я установил точку останова в деталях возврата и точку останова в detailsCompleted, но случилось то, что когда я вызвал веб-api GetDatails, он сначала выполнит return details и сразу после этого он выполнит функцию detailsCompleted. вот почему в настоящее время я не могу получить значение.
Комментарии:
1. В целом, для меня это выглядит как правильный шаблон обратного вызова, но у меня есть некоторые опасения по поводу того, почему некоторые вещи содержат слово «асинхронный» в своих названиях. Я подозреваю, что может отсутствовать какой-то важный код. Можете ли вы поделиться кодом внутри
DetailsAsync
?2. @BradleyUffner к сожалению, у меня нет кода для этого, все, что я знаю, это параметр, необходимый для использования DetailsAsync, но я думаю, что они вызывают службу с асинхронностью внутри нее.
3. @BradleyUffner на самом деле, когда устанавливается точка останова в DetailCompleted, в detail появляется нужная мне запись, но поскольку сначала выполнялась «return details», а не DetailCompleted, моя переменная details не получает записей.
4. Это то, что я подозревал. Вам нужен какой-то способ отложить выполнение
return
инструкции до тех пор, покаDetailCompleted
не будет получено сообщение о том, что результат получен.Semaphore
ИлиTaskCompletionSource
могло бы сработать, но может быть немного сложно настроить. Реальная проблема заключается в том, чтоDetailsAsync
реализован с плохим шаблоном асинхронности.5. @BradleyUffner насколько я понимаю, Semaphore или TaskCompletionSource могут быть реализованы в void, как DetailAsync, верно? поскольку у меня нет доступа к коду DetailsAsync
Ответ №1:
Возвращаемое значение a Action<T>
по умолчанию равно void. Если вы хотите возвращаемое значение, вы должны использовать Func<T,TResult>
https://learn.microsoft.com/en-us/dotnet/api/system.func-2?view=netframework-4.7.2
Комментарии:
1. Спасибо и да, я согласен с вами, проблема в том, что мне разрешено создавать только мою собственную функцию веб-api и использовать объект ServiceManager для получения записей, и, как я уже упоминал, я также не понимаю, почему сначала были заданы детали возврата, а затем функция DetailCompleted.
2. Когда вы говорите «по умолчанию void», вы имеете в виду, что оно когда-либо может быть не-void?
3. Он использует обратный вызов для «возврата» данных. Для того, чтобы это сработало, нет необходимости преобразовывать метод в
Func
.4. @pteberf это не может быть не-void, я думаю, я на самом деле хотел сказать «по определению»
5. @BradleyUffner Да, я думаю, вы правы, я неправильно понял вопрос. Ответ pteberf правильный
Ответ №2:
Я думаю, проблема здесь в том, что DetailsAsync()
, как подразумевается под name, является асинхронным, и вы возвращаете детали, прежде чем дождаться результатов DetailsAsync()
. Итак, вы должны await
это сделать, но из-за DetailsAsync
возврата void
вы не можете.
Таким образом, вы могли бы обернуть DetailsAsync
в задачу и .Wait()
для нее, но это своего рода отстой, потому что вы заблокируете вызывающий поток.
[HttpGet]
public IList<Detail> GetDetails()
{
Task.Run(() =>
ServiceManager.DetailsAsync("param1", "param2", detailsCompleted)
).Wait();
return details;
}
Комментарии:
1. Я прошу прощения за не очень четкое объяснение, ServiceManager. DetailAsync является недействительным, и внутри него, я думаю, они вызывают функцию async, вот почему я думаю, что они назвали ее DetailAsync
2. Ну, если они вызывают асинхронную функцию внутри и ожидают ее, скорее всего, DetailsAsync тоже асинхронен.
async
методы также могут возвращатьvoid
.3. @pteberf но когда я попытался реализовать приведенный вами код, он выдает ошибку «не удается дождаться void»
4. Если
DetailsAsync
реализовано, как я подозреваю, ожидание этого не поможет. Каково происхождениеDetailsAsync
? Является ли это частью какого-то общедоступного пакета или это внутренне написанный код, к которому у вас просто нет доступа?5. @BradleyUffner это внутренне написанный код, к которому у меня нет доступа.
Ответ №3:
Из-за того, как DetailsAsync
написано, вам понадобится какая-то система сигнализации, чтобы приостановить выполнение GetDetails
до тех пор, пока не будет запущен обратный вызов. Есть несколько вариантов, но я выбираю AutoResetEvent
, потому что с ним довольно просто работать и понимать.
(Я изменил некоторые возвращаемые типы только для того, чтобы мне не пришлось создавать поддельные классы, соответствующие вашему коду)
public class DetailController
{
private IList<int> details;
private AutoResetEvent callbackSignal = new AutoResetEvent(false);
private void DetailCompleted(IList<int> detail)
{
details = detail;
callbackSignal.Set();
}
public IList<int> GetDetails()
{
ServiceManager.DetailsAsync("param1", "param2", DetailCompleted);
callbackSignal.WaitOne();
return details;
}
}
callbackSignal.WaitOne();
будет блокироваться до тех пор, пока не будет «подан сигнал». В методе обратного вызова callbackSignal.Set();
отправляет сигнал, сообщающий всему, ожидающему события, что теперь можно продолжить.
Не зная точно, как DetailsAsync
это реализовано, я не могу гарантировать, что это сработает, но я надеюсь. Возможно, вам также придется добавить некоторую дополнительную защиту, чтобы убедиться, что это полностью реентерабельно, если требуется.
Если вы предпочитаете работать с более современным шаблоном async
/ await
, вы могли бы обернуть access в DetailsAsync
access в метод, который Task
возвращает и использует TaskCompletionSource
для организации обратного вызова и возвращаемых значений.
public class DetailController
{
public async Task<IList<int>> GetDetails()
{
var details = await ServiceWrapper.GetDetails();
return details;
}
}
public static class ServiceWrapper
{
public static Task<IList<int>> GetDetails()
{
var tcs = new TaskCompletionSource<IList<int>>();
ServiceManager.DetailsAsync("param1", "param2", (IList<int> details) =>
{
tcs.SetResult(details);
});
return tcs.Task;
}
}
Комментарии:
1. Спасибо за пример кода, я пробовал, но это занимает целую вечность, до сих пор он все еще работает.
2. Я добавил пример
TaskCompletionSource
шаблона, который может лучше подойти для вашего конкретного случая.3. ууууууууууу!!!!, большое спасибо, Брэдли, твой код для TaskCompletionSource действительно сработал!!! это заняло у меня целый день!!! еще раз спасибо!