Проходя Funcв Task.Run () в C # без использования лямбда-выражений - PullRequest
0 голосов
/ 21 марта 2019

Я пытаюсь разобраться с функциональностью Task / Func / Action / await в C #, но все еще нуждаюсь в вашей помощи:

У меня есть обработчик события нажатия кнопки в моем потоке графического интерфейса, который при вызове делаетследующее:

Func<int> t = new Func<int>(CountToBillion);
int result = await Task.Run(t);
//do something with result value...

Сам метод объявлен как:

private int CountToBillion()
{
    //count to 1 billion and return 0
}

Пока что он работает без ошибок.Но если я хочу передать параметр в CountToBillion (), то все, что я пытаюсь сделать, идет ужасно неправильно.

Func<int, int> t = new Func<int, int>(CountToBillion);
int result = Task.Run(t???);
// ...
private int CountToBillion(int workerId)
{
    //count to 1 billion and return 0
}

Сейчас я бы не хотел использовать лямбда-выражения, потому что я их пока не понимаю.Я всегда вижу это решение:

await Task.Run(() => methodcall(...));

Но должно быть далеко, чтобы использовать это без лямбда-выражений, или я совершенно не в курсе?Как бы я использовал Task.Run () с простыми старыми простыми объектами?

Ответы [ 2 ]

1 голос
/ 21 марта 2019

Я скептически отношусь к вашему желанию избегать использования анонимных методов или лямбда-выражений. Они удобны, идиоматичны, и функционально вы все равно будете делать что-то, по сути, то же самое, но без помощи компилятора.

Вы можете прочитать документы Task.Run() так же, как и все остальные, я полагаю, так что вы можете легко увидеть, что нет никакой перегрузки для этого метода, который обеспечивает параметризованный вызов делегата задачи. Так что вам нужно будет обеспечить это для себя.

Вы можете сделать это точно так же, как компилятор C # сделает это за вас, если вы захотите использовать лямбда-выражение. В частности, вам нужно будет объявить тип для хранения параметра, который имеет подходящий метод для вызова задачи.

Например:

class CountToBillionWrapper
{
    private readonly int _workerId;

    public CountToBillionWrapper(int workerId)
    {
        _workerId = workerId;
    }

    public int CountToBillion()
    {
        // do whatever, using the _workerId field as if it had been passed to the method
    }
}

Тогда вы можете сделать это:

CountToBillionWrapper wrapper = new CountToBillionWrapper(workerId);

int result = await Task.Run(wrapper.CountToBillion);    

Так как это, по сути, то, как компилятор C # реализует замыкание, которое будет необходимо при использовании лямбда-выражения, которое захватывает переменные, которые вы хотите передать в метод, я не вижу смысла делать это так путь. Похоже, дополнительная работа для трудного для чтения кода для меня.

Но, возможно, вы предпочитаете откровенность. Если так, то вышеприведенное сработает, чтобы выполнить то, что вы просите.

1 голос
/ 21 марта 2019

Метод Task.Run не имеет перегрузки, которая позволяет вам принять Func<T, R>.

  1. Вы можете использовать замыкание, но вы говорите, что этого не делаете.Я не хочу использовать, просто ради практики:

    var someInput = 42;
    
    // And there are side-effects to calling the Result property getter
    // but that's a totally different issue I am ignoring for now
    // because it depends on the application context
    var result = Task.Run(() => CountToBillion(someInput)).Result;
    
  2. Итак, реструктурируйте свой код.Делайте то, что компилятор C # делает с замыканиями.Сделайте это преобразование вручную.

    Поэтому вместо написания вашего CountToBillion метода, например, так:

    public static void Main(string[] args)
    {
    }
    
    static int CountToBillion(int someInput) { ... }
    

    Сделайте это:

    public static void Main(string[] args)
    {
      var foo = new Foo(42);
      var result = Task.Run(foo.CountToBillion).Result;
    }
    
    class Foo
    {
      public Foo(int someInput) { SomeInput = someInput; }
      public int SomeInput { get; set; }
      public int CountToBillion() { ... }
    }
    
...