How do i pass in a delegate as a parameter - c#

I want to pass in a void or an int/string/bool(Which returns a value) Dynamically like so.
Delay(MyVoid);//I wont to execute a delay here, after the delay it will execute the the param/void like so...
public static void MyVoid()
{
MessageBox.Show("The void has started!");
}
public async Task MyAsyncMethod(void V)
{
await Task.Delay(2000);
V()
}
ps, I have tried using Delegates but it doesn't let be use it as a parameter.

Use an Action delegate to execute a method which returns void:
public async Task MyAsyncMethod(Action V)
{
await Task.Delay(2000);
V();
}
Or Func<T> for a method which returns some value
public async Task MyAsyncMethod(Func<int> V)
{
await Task.Delay(2000);
int result = V();
}

Related

Pass Func<Task> as parameter

I have a method:
public async Task Method(Func<Task> action)
This function basically checks if few seconds have passed since the last run time.
I tried:
Func<Task> timer = async () => { await Task.Delay(3000);};
and invoking it like that
Func<Task> check = await _className.Method(timer);
However it says
Cannot implicitly convert 'void' to 'System.Func<System.Threading.Tasks.Task>'
Basically I am not able to pass a delayed task to a function.
Here's a trivial example of calling Method with a Func<Task>.
Func<Task> timer = async () => { await Task.Delay(3000);};
await Method(timer);
async Task Method(Func<Task> action)
{
await action();
}
I'm not sure exactly what you're trying to do, but this code works:
public class Program
{
public static async Task Main(string[] args)
{
var sw = Stopwatch.StartNew();
Func<Task> timer = async () => { await Task.Delay(3000); };
var theTask = Method(timer);
await theTask;
Console.WriteLine(sw.ElapsedMilliseconds); // ~3000
}
public static async Task Method(Func<Task> action)
{
await action();
}
}
I just put in var theTask = Method(timer); to illustrate that you can assign the return value to a Task before awaiting it.
Okay, so it works like tymtam already said - it looks like my problem was trying to assign end result to Func, i guess i need more sleep.
Thank you for the help tho
EDIT
I needed this:
Func<Task> check = async () => await _className.Method(timer);

Void task with generics in C#

I would like to reuse the following static generic Profile function in my application:
private static async Task<T> Profile<T>(Func<Task<T>> func, string operation)
{
Console.WriteLine($"{operation} is called");
return await func();
}
And I have the following interface to implement:
public interface ICustomerOperations
{
Task<Customer> GetCustomerAsync(string id);
Task DeleteCustomerAsync(string id);
}
I can use Profile method with GetCustomerAsync without any problem.
public async Task<Customer> GetCustomer(string id)
{
return await Profile(() => _customerOperations.GetCustomerAsync(id), $"GetCustomerAsync");
}
However when I try to use Profile with DeleteCustomerAsync
public async void DeleteCustomer(string id)
{
await Profile(() => _customerOperations.DeleteCustomerAsync(id), $"DeleteCustomerAsync");
}
Build failed:
The type arguments for method 'CrmService.Profile(Func<Task>,
string)' cannot be inferred from the usage. Try specifying the type
arguments explicitly.
So my question is how can I reuse Task<T> with void?
You can create an overload without duplicating the method:
private static Task Profile(Func<Task> func, string operation)
{
return Profile<object>(async () => { await func(); return null; }, operation);
}
A simple solution is to duplicate your method:
private static async Task Profile(Func<Task> func, string operation)
{
Console.WriteLine($"{operation} is called");
await func();
}
I think duplicating your method is cleaner than doing some hacks like this:
public async void DeleteCustomer(string id)
{
await Profile(async () => { await _customerOperations.DeleteCustomerAsync(id); return 0; }, $"DeleteCustomerAsync");
}

Function as parameter in async method

I call a method containing a function:
public void DoMagicStuff(Func<T> anyfunction) {
// do lots of magic stuff
}
This works:
public void DoNonAsyncStuff() {
DoMagicStuff(()=> {
AnotherFunction();
}
}
While this does not:
public async Task<CustomClass> DoAsynStuff() {
DoMagicStuff(()=> {
return await DoSomethingDifferent();
}
}
"The await operator can only be used in async functions"
How do I make this work for async methods?
If you intend to pass asynchronous delegates to DoMagicStuff, then you need to overload that with an asynchronous version:
public void DoMagicStuff(Func<T> anyfunction)
{
// do lots of magic stuff
T t = anyfunction();
}
public async Task DoMagicStuff(Func<Task> asyncfunction)
{
// do lots of magic stuff
T t = await asyncfunction();
}
This allows you to call await for the asyncfunction.
Any common logic can always be refactored into another method.
With regard to your question, await can only be used in a function that has been declared async, which your lambda hasn't.
It should be like this:
public async Task<CustomClass> DoAsynStuff()
{
await DoMagicStuff(async () =>
{
return await DoSomethingDifferent();
});
}
And in fact, because DoSomethingDifferent already returns a Task, the lambda is superfluous:
public async Task<CustomClass> DoAsynStuff()
{
await DoMagicStuff(DoSomethingDifferent);
}

await operator not stopping continuation of other method calls

I have three methods that look like below
private async Task checkPhotoLibraryAccess()
{
PHPhotoLibrary.RequestAuthorization(status =>
{
switch (status)
{
//stuff here
}
}
}
private async Task checkDeviceAuthorizationStatus()
{
var status = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);
switch (status)
{
//stuff here
}
}
private void displayAppBar()
{
AppBar.IsVisible = true;
}
I would like the execution of the first two methods to complete before calling the third. I have researched the issue and have found ways of doing so using the await operator and Wait() method. However my implementations has not worked. Here is my code below.
Attempt 1
private async void MyMethod()
{
await checkPhotoLibraryAccess();
await checkDeviceAuthorizationStatus();
displayAppBar(); //THIS IS CALLED BEFORE COMPLETION OF TWO ABOVE
}
Attempt 2
private async void MyMethod()
{
checkPhotoLibraryAccess().Wait();
checkDeviceAuthorizationStatus().Wait();
displayAppBar(); //SAME ISSUE
}
Any suggestions on how to get this to work?
To get a third method to be executed based on the completion of two other methods you use the WhenAll method of the Task class.
var t1 = CheckPhotoLibraryAccess();
var t2 = CheckDeviceAuthorization();
await Task.WhenAll(t1, t2).ContinueWith(t => DisplayAppBar());
#Yacoub Massad and #SLaks reminded me that in order for await to work on a method call, the method needs to contain an await operator.
So I changed my PhotoLibraryAccess method to contain an await operator and placed its method call right before the DisplayAppBar call.
private async Task checkPhotoLibraryAccess()
{
var status = await PHPhotoLibrary.RequestAuthorizationAsync();
//stuff here
}
And then ...
private async void MyMethod()
{
checkDeviceAuthorizationStatus(); //Removed async Task stuff
await checkPhotoLibraryAccess();
displayButtons(); //IT WORKS
}

c# Can a "task method" also be an "async" method?

I'm trying to get the hand of the new async CTP stuff and I'm probably confusing myself here..
I can have this "task method", with no problem:
public static Task<String> LongTaskAAsync() {
return Task.Run(() => {
return("AAA");
});
}
But what if I need the task to execute another task, can I mark it as "async" and use "await"? I tried this:
public async static Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return Task.Run(() => {
return("AAA");
});
}
But then mysteriously get this compiler error: Since this is an async method, the return expression must be of type 'string' rather than Task<string>
What am I missing here?
You may want to read my async/await intro post.
Return values from async methods are wrapped in a Task<TResult>. Likewise, await unwraps those return values:
public static async Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return await Task.Run(() => {
return("AAA");
});
}
The reasoning behind this is described in my Async "Why Do the Keywords Work That Way" Unofficial FAQ.
P.S. You can also use Task.FromResult for simple tests like this.
Edit: If you want to create and return the Task object itself, then the method should not be async. One somewhat common pattern is to have a public non-async method that calls the async portion only if necessary.
For example, some kind of asynchronous cache - if the object is in the cache, then return it immediately; otherwise, asynchronously create it, add it to the cache, and return it (this is example code - not thread-safe):
public static Task<MyClass> GetAsync(int key)
{
if (cache.Contains(key))
return Task.FromResult(cache[key]);
return CreateAndAddAsync(key);
}
private static async Task<MyClass> CreateAndAddAsync(int key)
{
var result = await CreateAsync(key);
cache.Add(key, result);
return result;
}
Can a “task method” also be an “async” method?
Yes it can be, by simply changing the method signature to public async static Task<Task<String>> LongTaskAAsync() since that is, what it will return.
If you use the async keyword, the runtime will wrap the type you return into a task, to enable asynchronousness. Say if you return a string, the runtime will wrap that into a Task<string>. int will go Task<int> and Task<string> will go Task<Task<string>>. See this console app to clearify:
public class Program
{
public static void Main(string[] args)
{
// start the main procedure asynchron
Task.Run(() => DoIt()).Wait();
}
// for async support since the static main method can't be async
public static async void DoIt()
{
Program p = new Program();
// use the methods
string s = await p.GetString();
int i = await p.GetInt();
Task<string> tsk = await p.GetTaskOfString();
// just to prove the task works:
// C# 5
string resultFromReturnedTask = await tsk;
// C# 4
string resultFromReturnedTask2 = tsk.Result;
}
public async Task<string> GetString()
{
return "string";
}
public async Task<int> GetInt()
{
return 6;
}
public async Task<Task<string>> GetTaskOfString()
{
return Task.Run(() => "string");
}
}

Categories