Passing a Func<int, int> to Task.Run() in C# without using lambda expressions

天涯浪子 提交于 2020-01-06 08:04:09

问题


I try to get my head around the Task/Func/Action/await functionality of C# but still need your help:

I have a button click event handler in my gui thread that, when called, does the following:

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

The method itself is declared as:

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

So far, this runs without error. But if I want to pass a parameter to CountToBillion() everything I try goes horribly wrong.

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
}

For now, I would like to NOT use lambda expressions, because I do not understand them yet. I always see this solution:

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

But there must be away to use this without lambda expressions, or am I completely off track here? How would I use Task.Run() with plain old simple objects?


回答1:


The Task.Run method doesn't have an overload that allows you to take a Func<T, R>.

  1. You can use a closure, but that's something you say you don't want to use, just for practice sake:

    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. So then, re-structure your code. Do what the C# compiler does to closures. Do that transformation yourself manually.

    So instead of writing your CountToBillion method like so:

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

    Do this:

    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() { ... }
    }
    



回答2:


I am skeptical of your desire to avoid the use of anonymous methods or lambda expressions. They are convenient, idiomatic, and functionally you're going to wind up doing something that is essentially the same anyway, but without the compiler's help.

You can read the Task.Run() docs as well as anyone else, I presume, so you can easily see that there's not any overload for that method that provides for a parameterized invocation of the task delegate. So you will need to provide that for yourself.

You can do it exactly the same way that the C# compiler would do it for you, if you'd be willing to use a lambda expression. In particular, you would need to declare a type to hold the parameter, and which has a suitable method to use for the task invocation.

For example:

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
    }
}

Then you can do this:

CountToBillionWrapper wrapper = new CountToBillionWrapper(workerId);

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

Since this is, essentially, how the C# compiler implements a closure that would be needed when using a lambda expression that captures the variables that you want to pass to the method, I don't really see the point in doing it this way. Seems like extra work for harder-to-read code to me.

But maybe you prefer the explicitness. If so, the above will work to do what you're asking.



来源:https://stackoverflow.com/questions/55274237/passing-a-funcint-int-to-task-run-in-c-sharp-without-using-lambda-expressio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!