How to pass async Task with parameters as a method argument? - c#

I have the following code but am getting the following errors
The first at line 21 is
"Delegate 'Func' does not take 2 arguments"
The second at line 28 is
"Argument 1: cannot convert from 'System.Threading.Tasks.Task' to 'System.Func<System.Threading.Tasks.Task>'"
I cannot figure out what I'm doing wrong here?
// Online C# Editor for free
// Write, Edit and Run your C# code using C# Online Compiler
using System;
using System.Threading.Tasks;
using System.Diagnostics;
public class HelloWorld
{
async Task Print1(string a, string b)
{
Debug.WriteLine("Printing!");
}
async Task PrintLoop(Func<Task> printer, int iterations = 3)
{
for (int i = 0; i < iterations; i++)
{
string c = "1";
await printer(c, c);
}
}
async Task Main()
{
string a ="";
string b = "";
await PrintLoop(Print1(a,b));
}
}

The type of your parameter printer in PrintLoop() needs to have the type of Func<string, string, Task> as your Print1() method has two Parameters of type string and a return value of type Task.
Have a look at the overload Func<T1, T2, TResult> in the docs.
Also, you need to pass the delegate, not call it. Here's a working example:
public class Program
{
static async Task Main(string[] args)
{
string a = "";
string b = "";
await PrintLoop(Print1);
}
async static Task Print1(string a, string b)
{
Debug.WriteLine("Printing!");
}
async static Task PrintLoop(Func<string, string, Task> printer, int iterations = 3)
{
for (int i = 0; i < iterations; i++)
{
string c = "1";
await printer(c, c);
}
}
}
BTW: as of now you do not use any asynchronous calls therefore there is no point in using Tasks. You also do not use the parameters supplied to Print1().

If you can't change the parameters of the PrintLoop then you have to do the following:
async Task PrintLoop(Func<Task> printer, int iterations = 3)
{
for (int i = 0; i < iterations; i++)
{
await printer();
}
}
The Func<Task> means it is a function without parameters that returns an awaitable Task
async Task Main()
{
string a ="";
string b = "";
await PrintLoop(() => Print1(a,b));
}
If you want to pass Print1 with parameters then you have to pass it by wrapping that into an anonymous method (lambda expression)

Related

I can not handle these classes c# uwp

until now I only have small applications, but now I can not handle these classes c# uwp
I want to break the code into several classes, each class will deal with a few parameters. classes will send parameter values to the main page. but when I try to display those parameters, they are always zero, even though the text file appears modified.
i have main page
namespace airflow
{
public sealed partial class MainPage : Page
{
}
public MainPage()
{
}
private async void main_page_Loaded(object sender, RoutedEventArgs e)
{
param_perimetrala read = new param_perimetrala();
ora_start_perimetrala = read.start_perimetrala;
var mesaj = new MessageDialog(ora_start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
}
and a class
namespace airflow
{
public class param_perimetrala
{
public static int ora_start_perimetrala;
public int minut_start_perimetrala;
public int ora_stop_perimetrala;
public int minut_stop_perimetrala;
public int ore_ciclu_perimetrala;
public int minut_ciclu_perimetrala;
public int contor_ore_perimetrala = 0;
public int contor_minute_perimetrala = 0;
public int contor_sec_perimetrala = 0;
public async void readfile_perimetrala()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile perimetrala_file = await folder.CreateFileAsync("parametrii_perimetrala.txt", CreationCollisionOption.OpenIfExists);
var readFile_perimetrala = await FileIO.ReadLinesAsync(perimetrala_file);
int count = 0;
foreach (var line in readFile_perimetrala)
{
string[] split_perimetrala = line.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
var temp = split_perimetrala[1];
if (count == 0)
{
ora_start_perimetrala = Int32.Parse(temp);
}
if (count == 1)
{
minut_start_perimetrala = Int32.Parse(temp);
}
if (count == 2)
{
ora_stop_perimetrala = Int32.Parse(temp);
}
count = count + 1;
}
}
public int start_perimetrala
{
get { return ora_start_perimetrala; }
set { ora_start_perimetrala = value; }
}
}
}
how to send ora_start_perimetrala value in mainpage?
enter image description here
In your main_page_Loaded event handler method you are calling read_file_perimetrala but read_file_perimetrala is async void. This means as soon as you get to an await statement, and the await is actually awaitable (not returned immediately) then the method will put the rest of the work aside until the awaited portion is complete. Then it picks back up and runs the rest. During that time of placing it aside the calling thread then continues to run wherever it's needed.
You need to make this a Task and await the call to is also so that you can insure the parameters are filled before continuing work.
Change your readFile_perimetrala to be like so:
public async Task readfile_perimetralaAsync()
Change the entire main_page_Loaded event handler to read like so...
private async void main_page_Loaded(object sender)
{
param_perimetrala read = new param_perimetrala();
await read.readfile_perimetralaAsync();
var mesaj = new MessageDialog(read.start_perimetrala.ToString());
var res = await mesaj.ShowAsync();
}
In your code you're assigning the value to a method but I can tell that you want the int value.
Just some light weight examples to help get started understanding Task and async await. Don't use this as a resource but just enough to get you curious to dig deeper because it's a simple but rather deep subject.
public class TaskExamples
{
public async void DoAsyncVoid()
{
await Task.Delay(200);
}
public async Task DoAsyncTask()
{
await Task.Delay(200);
}
public async Task<int> DoReturnValueTask()
{
await Task.Delay(200);
return 50;
}
public async void CallingTasks()
{
DoAsyncVoid(); //This can't use await because it is 'void' so the next line is ran as soon as this command reaches the first 'true awaitable' await.
await DoAsyncTask(); //This runs before DoAsyncVoid is complete.
var value = await DoReturnValueTask(); //This waits until 'DoAsyncTask' is complete because it is a Task and awaited.
await new MessageDialog(value.ToString()).ShowAsync(); //This waits until 'DoReturnValueTask' is complete and value will be 50 in this case.
//All code here waits until the Dialog is closed because it is also awaited.
}
}

Is there a generic way to make this conversion?

Below code works when I input it with a string like "15" but when I give it an integer like 15, it gives me error saying " Unable to cast object of type 'System.Int32' to type 'System.String".
How can I edit this code to allow for integer inputs also? Note: I make conversion inside for loop
using System;
using System.Threading;
namespace ConsoleApplication24
{
internal class Program
{
public static void ThreadMethod(object o)
{
for (int i = 0; i < Int32.Parse((string)o); i++)
{
Console.WriteLine("ThreadProc: {0}", i);
Thread.Sleep(0);
}
}
private static void Main(string[] args)
{
Thread t = new Thread(new ParameterizedThreadStart(ThreadMethod));
t.Start("15");
t.Join();
}
}
}
Try
public static void ThreadMethod(object o)
{
var limit = o is string ? Int32.Parse((string)o) : (int)o;
// OR var limit = Int32.Parse(o.ToString());
for (int i = 0; i < limit; i++)
{
Console.WriteLine("ThreadProc: {0}", i);
Thread.Sleep(0);
}
}
Just pass an int instead of a string and drop the Int32.Parse((string)o) part and cast o to int.
for (int i = 0; i < (int)o; i++)
If you want to support strings as well, you'll have to do some investigation (here using C# 7's pattern matching):
int val = /* some default */;
if (o is int oInt) val = oInt;
if (o is string oString) val = Int32.Parse(oString);
If you need more data than a single parameter, you'll have to pass an object (and cast that).
class Data
{
string oString { get; set; }
int oInt { get; set; }
}
There are a few things you can do here. By far the easiest is to not use that type of conversion. As you're going to a string, it is very easy:
for (int i = 0; i < Int32.Parse(o.ToString()); i++)...
However, a cleaner way to to that would be to overload your method:
public static void ThreadMethod(object o)
{
ThreadMethod(Int32.Parse(o.ToString()));
}
public static void ThreadMethod(int o)
{
for (int i = 0; i < o; i++)
{
Console.WriteLine("ThreadProc: {0}", i);
Thread.Sleep(0);
}
}
Even though that adds to the amount of code, it makes your methods far more readable. You can also separate out your call to the int method by doing checks before calling it. Oftentimes, you'll run into situations where you'll want to do more than just call the other method. Even still, this way removes the ambiguity and possibility of failure within the method that is doing what you want.

Run any C# method async. Possible?

With Task.Run() you can run any method async, like
public int Increase(int val)
{
return val + 1
}
int increased = await Task.Run ( () => return Increase(3) );
Is it possible to create an extension method, to just run any method as async like
var val = 1;
int increased = await Increased(3).AsAsync();
There is no way you could write an AsAsync method that would do what you're suggesting. In the code Increased(3).AsAsync() You have already synchronously executed Increased(3) and computed its result before AsAsync is called. It's too late to not execute that code before continuing.
If you have a CPU bound task and you want to use async as a convenient wrapper also for CPU bound operations, you can define an extension (vaguely based on this GetTask example)
public static class AsyncExtensions
{
public static Task<U> AsCpuBoundAsync<T,U>(this Func<T,U> func, T t)
{
return Task.Run(() => func(t));
}
}
to wrap a function
public static int Increase(int val)
{
//doing CPU bound activities...
return val + 1;
}
as follows
int increased = await ((Func<int,int>)(t =>
Increase(t))).AsCpuBoundAsync(3);

Async Method does not return control back to caller

Control is returned back to main method, before UploadToServer executes completely. Should I remove Task.Run() from UploadToServer or do a WaitAll explicitly?
public class Uploader
{
async public Task<int> Upload(int i)
{
int incremented = 0;
var t = UploadToServer(i);
if (t != null)
{
incremented = await t;
}
return incremented;
}
async private Task<int> UploadToServer(int i)
{
int incremented = 0;
await Task.Run(() =>
{
//Console.ReadLine();
//Actual upload operation
incremented = i + 1;
});
return incremented;
}
}
class Program
{
static void Main(string[] args)
{
Uploader upl = new Uploader();
var res = upl.Upload(10).Result;
}
}
When you await on async methods, the control is yielded back to the caller. What you're experiencing is proper async behavior.
If you dont want the method to return while the operation is executing, execute it synchronously.
If what you're attempting to do is I/O bound work (like upload something to a server), dont use Task.Run, as I/O bound is naturally exposed with async endpoints, eliminating the need for unnecessary threads in the process. Look at HttpClient as an example which exposes a bunch of XXXAsync methods.
try in this way
private Task<int> UploadToServer(int i)
{
int incremented = 0;
return Task.Run(() =>
{
//Console.ReadLine();
//Actual upload operation
return incremented = i + 1;
});
}
or in this way
private async Task<int> UploadToServer(int i)
{
return await Task.Run(() => DoSomething(i)).Wait();
}
private int DoSomething(int i)
{
//Console.ReadLine();
//Actual upload operation
return i+1;
}
Note that these examples aren't particularly useful methods.
considers that the background thread should live its own life.
In your case I suppose that maybe you can use some async methods of the HttpClient class of the framework
Example
private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.test.com/");
return html.Length;
}
UPDATE
I've tested this code and it worked
static void Main(string[] args)
{
Uploader upl = new Uploader();
var res = upl.Upload(10).Result;
}
public class Uploader
{
async public Task<int> Upload(int i)
{
int incremented = 0;
var t = UploadToServer(i);
if (t != null)
{
incremented = await t;
}
return incremented;
}
private async Task<int> UploadToServer(int i)
{
return await Task.Run(() => DoSomething(i));
}
private int DoSomething(int i)
{
//Console.ReadLine();
//Actual upload operation
Thread.Sleep(2000);
return i + 1;
}
}
main program waits 2 seconds before receive the right value and the control back.
Have a look at this Should I expose asynchronous wrappers for synchronous methods? article that explains when and how you should use asynchronous methods. In your specific example you aren't getting advantages from task.run. I have only provided to you one working example with your code.

Change normal loop to Parallel loop

I have the following code:
static void Main(string[] args)
{
TaskExecuter.Execute();
}
class Task
{
int _delay;
private Task(int delay) { _delay = delay; }
public void Execute() { Thread.Sleep(_delay); }
public static IEnumerable GetAllTasks()
{
Random r = new Random(4711);
for (int i = 0; i < 10; i++)
yield return new Task(r.Next(100, 5000));
}
}
static class TaskExecuter
{
public static void Execute()
{
foreach (Task task in Task.GetAllTasks())
{
task.Execute();
}
}
}
I need to change the loop in Execute method to paralle with multiple threads, I tried the following, but it isn't working since GetAllTasks returns IEnumerable and not a list
Parallel.ForEach(Task.GetAllTasks(), task =>
{
//Execute();
});
Parallel.ForEach works with IEnumerable<T>, so adjust your GetAllTasks to return IEnumerable<Task>.
Also .net has widely used Task class, I would avoid naming own class like that to avoid confusion.
Parallel.ForEach takes an IEnumerable<TSource>, so your code should be fine. However, you need to perform the Execute call on the task instance that is passed as parameter to your lambda statement.
Parallel.ForEach(Task.GetAllTasks(), task =>
{
task.Execute();
});
This can also be expressed as a one-line lambda expression:
Parallel.ForEach(Task.GetAllTasks(), task => task.Execute());
There is also another subtle bug in your code that you should pay attention to. Per its internal implementation, Parallel.ForEach may enumerate the elements of your sequence in parallel. However, you are calling an instance method of the Random class in your enumerator, which is not thread-safe, possibly leading to race issues. The easiest way to work around this would be to pre-populate your sequence as a list:
Parallel.ForEach(Task.GetAllTasks().ToList(), task => task.Execute());
This worked on my linqpad. I just renamed your Task class to Work and also returned an IEnumerable<T> from GetAllTasks:
class Work
{
int _delay;
private Work(int delay) { _delay = delay; }
public void Execute() { Thread.Sleep(_delay); }
public static IEnumerable<Work> GetAllTasks()
{
Random r = new Random(4711);
for (int i = 0; i < 10; i++)
yield return new Work(r.Next(100, 5000));
}
}
static class TaskExecuter
{
public static void Execute()
{
foreach (Work task in Work.GetAllTasks())
{
task.Execute();
}
}
}
void Main()
{
System.Threading.Tasks.Parallel.ForEach(Work.GetAllTasks(), new Action<Work>(task =>
{
//Execute();
}));
}

Categories

Resources