Pass delegate together with parameter to a function - c#

I want enqueue a list of tasks and then perform on certain event. Code:
internal class MyClass
{
private Queue<Task> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Task>();
}
public delegate bool Task(object[] args);
public void EnqueueTask(Task task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task.Invoke();
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}
Each delegate task may have their own list of parameters: Task(object[] args). My question is how to pass the parameter to each task for the task queue?

Okay, now we have a bit more information, it sounds like your EnqueueTask method should actually look like this:
public void EnqueueTask(Task task, object[] values)
Right?
For starters I would avoid using the name Task, which is already part of the core of .NET 4 and will become very prominent in .NET 5. As Joshua said, you've basically got a Func<object[], bool>.
Next, you could keep two lists - one for the delegates and one for the values, but it's easier just to keep a Queue<Func<bool>> like this:
private readonly Queue<Func<bool>> taskQueue = new Queue<Func<bool>>();
public void EnqueueTask(Task task, object[] values)
{
taskQueue.Enqueue(() => task(values));
}
Then the rest of your code will actually work "as is". The lambda expression there will capture values and task, so when you invoke the Func<bool>, it will supply those values to the original delegate.

Provided understanding your question correctly you just pass the information like a normal call. Have you considered using Func? You can just pass arguments to the Task.Invoke i.e. Task.Invoke([arguments here as a *single* object array]).
object[] arguments = null; // assign arguments to something
var workItemResult = task.Invoke(arguments);
Below is an example with the Func type.
internal class MyClass
{
private Queue<Func<object[], bool>> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Func<object[], bool>>();
}
public void EnqueueTask(Func<object[], bool> task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
object[] arguments = null; // assign arguments to something
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task(arguments);
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}

Related

Is it possible to infer this generic type, for type-safe callback?

Is there a way to get rid of the CS0411 error below, and not have to explicitly state the type?
Also do not want to have to use reflection.
var router = new ExampleRouter();
var controller = new ExampleWebController();
// compiles, but not elegant
router.MapPost<string>("/api/bar", controller.ProcessString);
// error CS0411: can't infer type
router.MapPost("/api/foo", controller.ProcessString);
class ExampleWebController {
public ExampleWebController() { }
public bool ProcessNumber(int v) { return true; }
public bool ProcessString(string v) { return true; }
}
class ExampleRouter {
public ExampleRouter() { }
public void MapPost<TBody>(string path, Func<TBody, bool> handler) {
// Save typeof(TBody), since TBody will actually be a class type we
// will construct for each callback
var body_type = typeof(TBody);
}
}
Yep, as someone's mentioned in comments one solution is to pass in the data as a parameter:
public void MapPost<TBody>(string path, Func<TBody, bool> handler, Tbody data) {
object dataType = data.GetType();
}
The reason your code is "inelegant" as you've said, is because the order of your generic arguments specifies an input type (TBody) and an output type (bool). However, in your calls to MapBody, you are only providing methods that return boolean results, so that the compiler doesn't know what to use for the value of TBody.
This is the origin of the CS0411 error you are receiving. The only way around it is to provide a generic type argument at the point of call.
This is why this code works, and should be what you use going forward:
var router = new ExampleRouter();
var controller = new ExampleWebController();
// compiles, but not elegant
router.MapPost<string>("/api/bar", controller.ProcessString);
A bit of a self answer here. If I change it to this, the MapPost() code looks elegant, which was my goal. HOWEVER, I have lost some compile time checking -- for example anything can be passed in as a "handler". I will post a new question on how I refine this.
var router = new ExampleRouter();
var controller = new ExampleWebController();
// We will have to do runtime validation that controller.ProcessString is a
// legal callback (which isn't ideal, but still fine).
// An improvement would be to add some kind of generic constraints?
router.MapPost("/api/foo", controller.ProcessString);
class ExampleWebController {
public ExampleWebController() { }
public bool ProcessNumber(int v) { return true; }
public bool ProcessString(string v) { return true; }
}
class ExampleRouter {
public ExampleRouter() { }
public void MapPost<TFunc>(string path, TFunc handler) {
var func_type = typeof(TFunc);
Console.WriteLine(func_type); // Prints "System.Func"
var args = func_type.GetGenericArguments();
foreach (var arg in args) {
// Prints "System.String", "System.Boolean"...awesome
Console.WriteLine(arg);
}
}
}

Generic Timer/Worker Function

I want to be able to perform workloads at intervals.
I want to be able to make this class generic so I can pass it whatever "workload" I want and my timer function just does it. I also would like a means of "returning" the workload response back to the caller.
As an example. Let's say I have a series of classes I have built that download data from a JSON API, or scrape a web page. This web scraper/API downloader needs to download pages from a site at different intervals. Each page will take a different number of parameters. I have found something online that indicates setting the Elapsed event to a delegate. This "may"work but I need to have the passed in delegate "dynamic" itself. So the Start method below which accepts a Func won't be correct from a "generic" standpoint, which is what I am after.
The solution itself is just an example of a line of thinking. Am open to other generic alternatives that help me achieve this.
public abstract class TimerWorkerDelegate : IDisposable, ITimerWorker
{
protected System.Timers.Timer DataTimer;
public virtual void Start(Func<string> callback,double interval)
{
DataTimer = new System.Timers.Timer();
DataTimer.Interval = interval;
DataTimer.Elapsed += delegate {
callback();
};
if (!DataTimer.Enabled)
DataTimer.Enabled = true;
//IDisposable code
}
}
I might not understand 100% what you are REALLY trying to achieve, but... maybe something like.
public class Worker<T>
{
public event EventHandler<T> OnCompleted;
public Worker()
{}
public Worker(Func<T> fn, int interval)
{
Func = fn;
Interval = interval;
}
public async void Start()
{
if (Func == null)
throw new ArgumentNullException();
while (true)
{
await Task.Delay(Interval);
try
{
var result = Func();
OnCompleted(this, result);
}
catch
{
return; // handle
}
}
}
public Func<T> Func { get; set; }
public int Interval { get; set; }
}
And then usage in Console tester app as
public static void Main(string[] args)
{
var worker = new Worker<string>
{
Interval = 1000,
Func = () => { return string.Format("did some work at {0}", DateTime.Now); }
};
worker.OnCompleted += (sender, result) => { Console.WriteLine(result); };
worker.Start();
Console.ReadLine();
}
If you're open to using a library you could look at System.Reactive
With it you could setup something very easily to accomplish what you are looking to do.
Below is a very rudimentary implementation of something that could work for you:
void Main()
{
var scheduled = Schedule(
TimeSpan.FromSeconds(1),
() => Console.WriteLine($"The current time is: {DateTime.Now}"));
Console.ReadLine();
// Dispose will stop the scheduled action
scheduled.Dispose();
}
public IDisposable Schedule<T>(TimeSpan interval, Func<T> func)
=> Observable.Interval(interval).Subscribe(_ => func());
public IDisposable Schedule(TimeSpan interval, Action action)
=> Observable.Interval(interval).Subscribe(_ => action());

Why is Visual Studio 2010 telling me "'System.Delegate' does not contain a definition for 'EndInvoke'"?

Why is Visual Studio 2010 telling me "'System.Delegate' does not contain a definition for 'EndInvoke'" when I call job.Delegate.EndInvoke()? How do I fix it? Note that it likes BeginInvoke() just fine, and doesn't complain if I add EndInvoke() immediately after BeginInvoke() (thought hat doesn't accomplish what I want).
I have a little JobTracker class for tracking backup jobs:
public class JobTracker
{
private class Job
{
public Account Account { get; set; }
public IAsyncResult Result { get; set; }
public Delegate Delegate { get; set; }
public bool IsCompleted { get { return result.IsCompleted } }
public string Instance { get { return Account.Instance } }
}
public List<Job> Running = new List<Job>;
public void AddJob(Account a, IAsyncResult result, Delegate del)
{
var j = new Job { Account = a, Result = result, Delegate = del };
Running.Add(j);
}
public void RemoveJob(Job job)
{
Running.Remove(job);
}
public bool IsInstanceRunning(string instance)
{
return (Running.Count(x => x.Instance == instance) > 0);
}
}
These backup jobs will happen asynchronously via BeginInvoke()/EndInvoke(). The calling code (simplified) looks something like this:
public void BackupAccounts(IEnumerable<Account> accounts, int maxconcurrent = 4)
{
// local copy
List<Accounts> myaccounts = accounts.ToList();
var jobs = new JobTracker();
while (myaccounts.Count > 0)
{
// check our running jobs
foreach (var job in jobs.Running)
{
if (job.IsCompleted)
{
// PROBLEM LINE:
job.Delegate.EndInvoke();
jobs.RemoveJob(job);
}
}
// more jobs!
while (jobs.Count < maxconcurrent)
{
int i = 0;
Account account = null;
// choose the next account on a free instance
while (int < accounts.Count)
{
account = accounts[i];
// instance in use?
if (jobs.InstanceIsRunning(account.Instance))
{
i += 1;
continue;
}
else
{
// start the job
accounts.RemoveAt(i);
BackupDelegate del = new BackupDelegate(BackupAccount, account);
IAsyncResult result = del.BeginInvoke();
jobs.AddJob(account, result, del);
}
}
}
// we're all full up, give it some time to work
Thread.Sleep(2000);
}
}
PS - I know this code can be greatly simplified. It's a first, get-it-working iteration -- I just can't figure out why VS doesn't like it.
When you call BeginInvoke, you're calling it on a specific subclass of Delegate. When you call EndInvoke, you're trying to call it on System.Delegate itself, which won't work. Each subclass declares its own Invoke/BeginInvoke/EndInvoke set of methods - which it has to, given that the signatures of the methods varies according to the signature of the exact delegate type you're talking about. If you look at the documentation for System.Delegate you won't find any of those methods there.
It's not really clear what your code is trying to achieve, but if you want to call EndInvoke, you'll need to make Job.Delegate a specific delegate type.
Because EndInvoke doesn't exist on Delegate. Instead you should have your Job class hold a reference to a BackupDelegate.

Async result handle to return to callers

I have a method that queues some work to be executed asynchronously. I'd like to return some sort of handle to the caller that can be polled, waited on, or used to fetch the return value from the operation, but I can't find a class or interface that's suitable for the task.
BackgroundWorker comes close, but it's geared to the case where the worker has its own dedicated thread, which isn't true in my case. IAsyncResult looks promising, but the provided AsyncResult implementation is also unusable for me. Should I implement IAsyncResult myself?
Clarification:
I have a class that conceptually looks like this:
class AsyncScheduler
{
private List<object> _workList = new List<object>();
private bool _finished = false;
public SomeHandle QueueAsyncWork(object workObject)
{
// simplified for the sake of example
_workList.Add(workObject);
return SomeHandle;
}
private void WorkThread()
{
// simplified for the sake of example
while (!_finished)
{
foreach (object workObject in _workList)
{
if (!workObject.IsFinished)
{
workObject.DoSomeWork();
}
}
Thread.Sleep(1000);
}
}
}
The QueueAsyncWork function pushes a work item onto the polling list for a dedicated work thread, of which there will only over be one. My problem is not with writing the QueueAsyncWork function--that's fine. My question is, what do I return to the caller? What should SomeHandle be?
The existing .Net classes for this are geared towards the situation where the asynchronous operation can be encapsulated in a single method call that returns. That's not the case here--all of the work objects do their work on the same thread, and a complete work operation might span multiple calls to workObject.DoSomeWork(). In this case, what's a reasonable approach for offering the caller some handle for progress notification, completion, and getting the final outcome of the operation?
Yes, implement IAsyncResult (or rather, an extended version of it, to provide for progress reporting).
public class WorkObjectHandle : IAsyncResult, IDisposable
{
private int _percentComplete;
private ManualResetEvent _waitHandle;
public int PercentComplete {
get {return _percentComplete;}
set
{
if (value < 0 || value > 100) throw new InvalidArgumentException("Percent complete should be between 0 and 100");
if (_percentComplete = 100) throw new InvalidOperationException("Already complete");
if (value == 100 && Complete != null) Complete(this, new CompleteArgs(WorkObject));
_percentComplete = value;
}
public IWorkObject WorkObject {get; private set;}
public object AsyncState {get {return WorkObject;}}
public bool IsCompleted {get {return _percentComplete == 100;}}
public event EventHandler<CompleteArgs> Complete; // CompleteArgs in a usual pattern
// you may also want to have Progress event
public bool CompletedSynchronously {get {return false;}}
public WaitHandle
{
get
{
// initialize it lazily
if (_waitHandle == null)
{
ManualResetEvent newWaitHandle = new ManualResetEvent(false);
if (Interlocked.CompareExchange(ref _waitHandle, newWaitHandle, null) != null)
newWaitHandle.Dispose();
}
return _waitHandle;
}
}
public void Dispose()
{
if (_waitHandle != null)
_waitHandle.Dispose();
// dispose _workObject too, if needed
}
public WorkObjectHandle(IWorkObject workObject)
{
WorkObject = workObject;
_percentComplete = 0;
}
}
public class AsyncScheduler
{
private Queue<WorkObjectHandle> _workQueue = new Queue<WorkObjectHandle>();
private bool _finished = false;
public WorkObjectHandle QueueAsyncWork(IWorkObject workObject)
{
var handle = new WorkObjectHandle(workObject);
lock(_workQueue)
{
_workQueue.Enqueue(handle);
}
return handle;
}
private void WorkThread()
{
// simplified for the sake of example
while (!_finished)
{
WorkObjectHandle handle;
lock(_workQueue)
{
if (_workQueue.Count == 0) break;
handle = _workQueue.Dequeue();
}
try
{
var workObject = handle.WorkObject;
// do whatever you want with workObject, set handle.PercentCompleted, etc.
}
finally
{
handle.Dispose();
}
}
}
}
If I understand correctly you have a collection of work objects (IWorkObject) that each complete a task via multiple calls to a DoSomeWork method. When an IWorkObject object has finished its work you'd like to respond to that somehow and during the process you'd like to respond to any reported progress?
In that case I'd suggest you take a slightly different approach. You could take a look at the Parallel Extension framework (blog). Using the framework, you could write something like this:
public void QueueWork(IWorkObject workObject)
{
Task.TaskFactory.StartNew(() =>
{
while (!workObject.Finished)
{
int progress = workObject.DoSomeWork();
DoSomethingWithReportedProgress(workObject, progress);
}
WorkObjectIsFinished(workObject);
});
}
Some things to note:
QueueWork now returns void. The reason for this is that the actions that occur when progress is reported or when the task completes have become part of the thread that executes the work. You could of course return the Task that the factory creates and return that from the method (to enable polling for example).
The progress-reporting and finish-handling are now part of the thread because you should always avoid polling when possible. Polling is more expensive because usually you either poll too frequently (too early) or not often enough (too late). There is no reason you can't report on the progress and finishing of the task from within the thread that is running the task.
The above could also be implemented using the (lower level) ThreadPool.QueueUserWorkItem method.
Using QueueUserWorkItem:
public void QueueWork(IWorkObject workObject)
{
ThreadPool.QueueUserWorkItem(() =>
{
while (!workObject.Finished)
{
int progress = workObject.DoSomeWork();
DoSomethingWithReportedProgress(workObject, progress);
}
WorkObjectIsFinished(workObject);
});
}
The WorkObject class can contain the properties that need to be tracked.
public class WorkObject
{
public PercentComplete { get; private set; }
public IsFinished { get; private set; }
public void DoSomeWork()
{
// work done here
this.PercentComplete = 50;
// some more work done here
this.PercentComplete = 100;
this.IsFinished = true;
}
}
Then in your example:
Change the collection from a List to a Dictionary that can hold Guid values (or any other means of uniquely identifying the value).
Expose the correct WorkObject's properties by having the caller pass the Guid that it received from QueueAsyncWork.
I'm assuming that you'll start WorkThread asynchronously (albeit, the only asynchronous thread); plus, you'll have to make retrieving the dictionary values and WorkObject properties thread-safe.
private Dictionary<Guid, WorkObject> _workList =
new Dictionary<Guid, WorkObject>();
private bool _finished = false;
public Guid QueueAsyncWork(WorkObject workObject)
{
Guid guid = Guid.NewGuid();
// simplified for the sake of example
_workList.Add(guid, workObject);
return guid;
}
private void WorkThread()
{
// simplified for the sake of example
while (!_finished)
{
foreach (WorkObject workObject in _workList)
{
if (!workObject.IsFinished)
{
workObject.DoSomeWork();
}
}
Thread.Sleep(1000);
}
}
// an example of getting the WorkObject's property
public int GetPercentComplete(Guid guid)
{
WorkObject workObject = null;
if (!_workList.TryGetValue(guid, out workObject)
throw new Exception("Unable to find Guid");
return workObject.PercentComplete;
}
The simplest way to do this is described here. Suppose you have a method string DoSomeWork(int). You then create a delegate of the correct type, for example:
Func<int, string> myDelegate = DoSomeWork;
Then you call the BeginInvoke method on the delegate:
int parameter = 10;
myDelegate.BeginInvoke(parameter, Callback, null);
The Callback delegate will be called once your asynchronous call has completed. You can define this method as follows:
void Callback(IAsyncResult result)
{
var asyncResult = (AsyncResult) result;
var #delegate = (Func<int, string>) asyncResult.AsyncDelegate;
string methodReturnValue = #delegate.EndInvoke(result);
}
Using the described scenario, you can also poll for results or wait on them. Take a look at the url I provided for more info.
Regards,
Ronald
If you don't want to use async callbacks, you can use an explicit WaitHandle, such as a ManualResetEvent:
public abstract class WorkObject : IDispose
{
ManualResetEvent _waitHandle = new ManualResetEvent(false);
public void DoSomeWork()
{
try
{
this.DoSomeWorkOverride();
}
finally
{
_waitHandle.Set();
}
}
protected abstract DoSomeWorkOverride();
public void WaitForCompletion()
{
_waitHandle.WaitOne();
}
public void Dispose()
{
_waitHandle.Dispose();
}
}
And in your code you could say
using (var workObject = new SomeConcreteWorkObject())
{
asyncScheduler.QueueAsyncWork(workObject);
workObject.WaitForCompletion();
}
Don't forget to call Dispose on your workObject though.
You can always use alternate implementations which create a wrapper like this for every work object, and who call _waitHandle.Dispose() in WaitForCompletion(), you can lazily instantiate the wait handle (careful: race conditions ahead), etc. (That's pretty much what BeginInvoke does for delegates.)

Anonymous method as parameter to BeginInvoke?

Why can't you pass an anonymous method as a parameter to the BeginInvoke method? I have the following code:
private delegate void CfgMnMnuDlg(DIServer svr);
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke((CfgMnMnuDlg)ConfigureMainMenu,
new object[] { server});
}
else
{
// Do actual work here
}
}
I'm trying to avoid declaring the delegate. Why can't I write something like the below instead? Or can I, and I just can't figure out the correct syntax? The below currently generates an:
Argument type 'Anonymous method' is not assignable to parameter type 'System.Delegate'
Ok, that's right of course, but is there some other syntax I can use to do this (avoid having to declare a separate delegate in order to use BeginInvoke()?
(Being able to do this would fit in neatly with the concept of using anon methods/lamdas in place of explicit delegates which works so cleanly everywhere else.)
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke( // pass anonymous method instead ?
delegate(DIServer svr) { ConfigureMainMenu(server);},
new object[] { server});
}
else
{
// Do actual work here
}
}
Try this:
control.BeginInvoke((MethodInvoker) delegate { /* method details */ });
Or:
private void ConfigureMainMenu(DIServer server)
{
if (control.InvokeRequired)
{
control.BeginInvoke(new Action<DIServer >(ConfigureMainMenu), server);
}
else
{
/* do work */
}
}
Or:
private void ConfigureMainMenu(DIServer server)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
// Private variable
_methodInvoker = new MethodInvoker((Action)(() => ConfigureMainMenu(server)));
_methodInvoker.BeginInvoke(new AsyncCallback(ProcessEnded), null); // Call _methodInvoker.EndInvoke in ProcessEnded
}
else
{
/* do work */
}
}
You should be able to write something like this:
private void ConfigureMainMenu(DIServer server,)
{
MenuStrip mnMnu = PresenterView.MainMenu;
if (mnMnu.InvokeRequired)
{
mnMnu.BeginInvoke(new Action<DIServer>(ConfigureMainMenu),
new object[] { server});
}
else
{
// Do actual work here
}
}
You could write an extension method that would wrap anonymous methods, and even take care of the InvokeRequired semantics:
public static void InvokeAction(this Control ctl, Action a)
{
if (!ctl.InvokeRequired)
{
a();
}
else
{
ctl.BeginInvoke(new MethodInvoker(a));
}
}
This would allow you to do:
control.InvokeAction(delegate() { ConfigureMainMenu(server); });
You can do this in a single method by calling invoking yourself:
ClassData updData = new ClassData();
this.BeginInvoke(new Action<ClassData>(FillCurve),
new object[] { updData });
...
public void FillCurve(ClassData updData)
{
...
}
For completely anonymous methods with a limited number of parameters:
Func<int, int?> caller = new Func<int, int?>((int param1) =>
{
return null;
});
caller.BeginInvoke(7, new AsyncCallback((IAsyncResult ar) =>
{
AsyncResult result = (AsyncResult)ar;
Func<int, int?> action = (Func<int, int?>)result.AsyncDelegate;
action.EndInvoke(ar);
}), null);
You can use one of the other Func delegate types as needed.
I've tried a bunch of different methods but none work. ie...
// Fails -- cannot convert lamda to System.Delegate
mnMnu.BeginInvoke( (DIServer svr)=> {ConfigureMainMenu(server);}, new object[] server);
// Fails -- cannot convert anonymous method to System.Delegate
mnMnu.BeginInvoke( new delegate(DIServer svr){ConfigureMainMenu(server);}, new object[] server);
So, the short answer is no. You could create short helper delegates in the given context and use lambdas to make it a bit neater but that's pretty much it.
EDIT: Turns out I'm wrong. The methodinvoker answer below works.
See this page

Categories

Resources