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
Related
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);
}
}
}
Now I have a ConcurrentDictionary .
I want to invoke the IDataExchangeServiceCallBack's method.
the IDataExchangeServiceCallBack's code below:
[ServiceContract]
public interface IDataExchangeServiceCallBack
{
[OperationContract(IsOneWay = true)]
void SendResult(string msg);
[OperationContract(IsOneWay = true)]
void Receive(List<RealDataModel> models);
}
In other class, I want invoke dict's method foreach.
such as
public void Receive(List<RealDataModel> models)
{
Broast(o => nameof(o.Receive), models);
}
public void SendResult(string msg)
{
Broast(o => nameof(o.SendResult), msg);
}
And Broast method below:
private void Broast(Func<IDataExchangeServiceCallBack, string> funcMethodName, params object[] args)
{
if (_callbackChannelList.Count > 0)
{
var callbackChannels = _callbackChannelList.ToArray();
foreach (var channel in callbackChannels)
{
try
{
var type = channel.Value.GetType();
// fetch the method's name.
var methodName = funcMethodName.Invoke(channel.Value);
// reflect & get the method
var methodInfo = type.GetMethod(methodName);
//invoke
methodInfo?.Invoke(channel.Value, args);
}
catch (Exception ex)
{
_callbackChannelList.TryRemove(channel.Key, out _);
}
}
}
}
Now my question is how to implement above code without reflect, it's there any better solution.
The Expression can implement it ?
I'm not familiar with Expression.
Thanks.
I don't understand how you got to the solution you have. Regardless of the type of o, the expression, for example, nameof(o.Receive) will always result in the string "Receive". It seems to me you could just pass nameof(IDataExchangeServiceCallBack.Receive) to the method instead of passing a delegate.
That said, I also don't understand why you are looking to use reflection or Expression. It appears to me that, at the call site, you know the type of object you're dealing with, the method you want to call, and the arguments you want to pass. So you could just pass a delegate that does all that. For example:
private void Broast(Action<IDataExchangeServiceCallBack> callback)
{
foreach (var channel in _callbackChannelList.ToArray())
{
try
{
//invoke
callback(channel.Value);
}
catch (Exception ex)
{
_callbackChannelList.TryRemove(channel.Key, out _);
}
}
}
Used like:
public void Receive(List<RealDataModel> models)
{
Broast(o => o.Receive(models));
}
public void SendResult(string msg)
{
Broast(o => o.SendResult(msg));
}
Note that I've cleaned up the Broast() method a bit. There's very little overhead calling ToArray() on an empty collection, and it simplifies the code significantly to remove the Count > 0 check. Code is always easier to write and read later if you can remove unnecessary conditional checks.
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;
}
}
I'm using this to change something on other thread:
MethodInvoker m = () => { login_submit.Text = "Login"; };
if (InvokeRequired)
{
BeginInvoke(m);
}
else
{
Invoke(m);
}
this is working fine.
How can I pass argumets to that lamba expression?
I want to do sth like that:
MethodInvoker m = (string txt) => { login_submit.Text = txt; };
if (InvokeRequired)
{
BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
Invoke(m); // I need to pass txt string in some way here.
}
If this is a common scenario for you, I suggest writing an extension method:
public static class ControlExtensions
{
public static void EnsureInvokeAsync(this Control control, Action action)
{
if (control.InvokeRequired) control.BeginInvoke(action);
else action();
}
}
class MyControl : UserControl
{
void M(string s)
{
// the lambda will capture the string in a closure
// the compiler does all the hard work for you
this.EnsureInvokeAsync(() => _button.Text = s);
}
}
Also, you should look into using BackgroundWorker or tasks for async operations.
If InvokeRequired is false then you don't need to worry about invoking anything at all - you're already on the right thread.
A better solution might be something like this:
public delegate void InvokerDelegate(string data);
public void DoStuff(string data){
login_submit.Text = data;
}
and then when calling it do:
if (InvokeRequired){
Invoke(InvokerDelegate(DoStuff), "something");
}
else{
DoStuff("Something");
}
A fairly common pattern you will see is to do something like this for functions that manipulate the GUI in a multithreaded environment
public delegate void InvokerDelegate();
public void DoGuiStuff(){
if (login_submit.InvokeRequired){
login_submit.Invoke(InvokerDelegate(DoGuiStuff));
return;
}
login_submit.Text = "Some value";
}
If you use the above pattern the function checks to see if an invoke is required and if so Invokes itself on the right thread. It then returns. When it invokes itself the check to see if an invoke is required returns false so it doesn't bother invoking itself again - it just runs the code.
Edit: I just went back to winforms and tried to use that pattern only to spend a couple of frustrating minutes trying to work out why I couldn't invoke a lambda. I thought I'd better come back and update this answer to add the required casting in case anyone else tried to use it.
MethodInvoker is a delegate type that doesn't have any parameters. If I understand you correctly, you can do it like this:
string txt = "some text";
MethodInvoker m = () => { login_submit.Text = txt; };
You can use closures to pass the value into the lambda's body.
string value = "Login";
MethodInvoker m = () => { login_submit.Text = value; };
if (InvokeRequired)
{
BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
Invoke(m); // I need to pass txt string in some way here.
}
or you can use class member's data
I have a class 'KeyEvent'; one of which's members is:
public delegate void eventmethod(object[] args);
And the method passed to the object in the constructor is stored in this member:
private eventmethod em;
Constructor:
public KeyEvent(eventmethod D) {
em = D;
}
public KeyEvent(eventmethod D, object[] args) : this(D) {
this.args = args;
}
public KeyEvent(Keys[] keys, eventmethod D, object[] args) : this(keys, D) {
this.args = args;
}
The 'eventmethod' method is then called by using the public method "ThrowEvent":
public void ThrowEvent() {
if (!repeat && thrown) return;
em.DynamicInvoke(args);
this.thrown = true;
}
As far as I can see, this compiles fine. But when trying to create an instance of this class (KeyEvent), I'm doing something wrong. This is what I have so far:
object[] args = {new Vector2(0.0f, -200.0f)};
Keys[] keys = { Keys.W };
KeyEvent KeyEvent_W = new KeyEvent(keys, new KeyEvent.eventmethod(GameBase.ChangeSquareSpeed), args);
GameBase.ChangeSquareSpeed doesn't do anything at the moment, but looks like this:
static public void ChangeSquareSpeed(Vector2 squarespeed) {
}
Anyway, the erroneous line is this one:
KeyEvent KeyEvent_W = new KeyEvent(keys, new KeyEvent.eventmethod(GameBase.ChangeSquareSpeed), args);
The error that the compiler gives me is:
error CS0123: No overload for 'ChangeSquareSpeed' matches delegate 'BLBGameBase.KeyEvent.eventmethod'
My question is: Does this mean I have to change ChangeSquareSpeed to take no parameters (in which case, what is a better way of doing this?), or am I doing something syntactically wrong?
Thank you in advance.
I think the error is very explicit. Your ChangeSquareSpeed method doesn't match the delegate . The delegate expects a method with one object[] as parameter but your passing a method with a Vector2 as a parameter, hence the error.
Try this method:
static public void ChangeSquareSpeed(object[] squarespeed)
{}
(update)
I see some confusion in your code, specially in the line:
object[] args = {new Vector2(0.0f, -200.0f)};
I can't really understand if you want an array of Vector2's or just a Vector2's object.
If you pretend to have an array of Vector2's I think this might seem reasonable:
Change the delegate to:
public delegate void eventmethod(Vector2 args);
and then
public void ThrowEvent() {
if (!repeat && thrown) return;
foreach(object obj : args)
{
em.DynamicInvoke((Vector2)obj);
}
this.thrown = true;
}
(update 2)
In that case, I think you should create a generic version of KeyEvent. See this example and go from there:
class KeyEvent<T>
{
public T Args { get; set; }
public Action<T> A { get; set; }
public KeyEvent() { }
public void ThrowEvent()
{
A.DynamicInvoke(Args);
}
}
// ...
static void M1(object[] o)
{
Console.WriteLine("M1 {0}", o);
}
static void M2(Vector2 v)
{
Console.WriteLine("M2 {0}", v);
}
static void Main(string[] args)
{
KeyEvent<object[]> e1 = new KeyEvent<object[]>
{
A = new Action<object[]>(M1),
Args = new object[] {};
};
KeyEvent<Vector2> e2 = new KeyEvent<Vector2>
{
A = new Action<Vector2>(M2),
Args = new Vector2();
};
}
The delegate eventmethod states that all events using it should take object[] (args) as their only in parameter. Depending on what you're using this code for, you want to either:
Change the signature of ChangeSquareSpeed to ChangeSquareSpeed(object[] squarespeed)
Create a new delegate, with the signature void neweventmethod(Vector2 args); and use that
Change the signature of eventmethod to the above
If you are on C# 3, change the delegate to an Action<object[]>. That will make your life much simpler, as it will be type-safe to invoke it.
That would allow you to simply invoke it like this:
this.em(args);
and you would have compile-time checking instead.