Интерфейс с методом содержит ValueTuple - PullRequest
0 голосов
/ 02 апреля 2020

У меня есть интерфейс:

public Interface Inter1
{
 ....
 Task<(ITestBulk testBulk, bool resultBulk)> GetBulk(int par1, int par2, string par3);
}

Затем есть какой-то метод:

 class MyClass{

   internal bool m_resultBulk;

 internal async void NewBulk()
 {
   ....
   (ITestBulk, m_resultBulk) = await service.GetBulk(1, 2, "ggg");
 } 
}

Но m_resultBulk не может быть найден. Что я делаю не так?

Ответы [ 2 ]

4 голосов
/ 02 апреля 2020

(Как примечание: ваш NewBulk метод должен вернуть Task, иначе его вызывающие не могут ожидать его завершения - важно избегать кода, который запускает "запустить и забыть" asyn c logi c потому что становится невозможным безопасно очистить после завершения асинхронного c (или правильно перехватить ошибки) - единственное исключение - обработчики событий в WinForms и WPF - и это потому, что их контексты синхронизации будут правильно обрабатываться любые неудачные продолжения. - Кроме того, методы asyn c обычно должны иметь имена, заканчивающиеся суффиксом ...Async.

В любом случае, ваша главная проблема в том, что ваш call-сайт для GetBulk (который должен иметь имя GetBulkAsync) неправильно объявляет ValueTuple возвращение локального.

Вместо этого вы хотите:

class MyClass
{
    private bool lastNewBulkResult;

    internal async Task NewBulkAsync()
    {
        (ITestBulk testBulk, bool resultBulk) = await this.service.GetBulkAsync( 1, 2, "ggg" ); // Specify `ConfigureAwait` as appropriate too.

        this.lastNewBulkResult = resultBulk;
    }
}

Однако у этого кода есть проблемы: продолжение после GetBulkAsync может потенциально работает в любом потоке - это означает, что состояние вашего MyClass экземпляра (то есть его поля) видоизменяется в thread-unsa Fe образом. Решением этой проблемы является перепроектирование ваших классов, чтобы они полагались на неизменное состояние и представляли изменения в состоянии через параметры метода и возвращаемые значения (или асинхронные c результаты), что полностью исключает проблемы безопасности потоков.

Относительно смешивания ValueTuple и await:

Это утверждение:

(ITestBulk testBulk, bool resultBulk) = await this.service.GetBulkAsync( 1, 2, "ggg" );

Эквивалентно этому:

Task<(ITestBulk testBulk, bool resultBulk)> task = this.service.GetBulkAsync( 1, 2, "ggg" );
(ITestBulk testBulk, bool resultBulk) = await task;

И также эквивалентно этому:

Task<(ITestBulk testBulk, bool resultBulk)> task = this.service.GetBulkAsync( 1, 2, "ggg" );
(ITestBulk testBulk, bool resultBulk) tupleValue = await task;
ITestBulk testBulk = tupleValue.testBulk;
bool resultBulk = tupleValue.resultBulk;

Или это (используя var для вывода типа объявления):

var task = this.service.GetBulkAsync( 1, 2, "ggg" );
var tupleValue = await task;
ITestBulk testBulk = tupleValue.testBulk;
bool resultBulk = tupleValue.resultBulk;

Или это:

var tupleValue = await this.service.GetBulkAsync( 1, 2, "ggg" );
ITestBulk testBulk = tupleValue.testBulk;
bool resultBulk = tupleValue.resultBulk;
4 голосов
/ 02 апреля 2020

В строке

 (ITestBulk, m_resultBulk) = await service.GetBulk(1, 2, "ggg");

в кортеже с левой стороны отсутствует имя переменной для первого элемента кортежа. Если вам не нужно значение ITestBulk, вы можете использовать сброс таким образом:

(_, m_resultBulk) = await service.GetBulk(1, 2, "ggg");

Если вам это нужно, вам нужно объявить переменную (потому что вы не можете смешивать объявление и деконструкцию в кортежах) :

ITestBulk testBulk;
(testBulk, m_resultBulk) = await service.GetBulk(1, 2, "ggg");
...